XML serialization query - c#

I have the following XML which needs deserializing/serializing:
<instance>
<dog>
<items>
<item>
<label>Spaniel</label>
</item>
</items>
</dog>
<cat>
<items>
<item>
<label>Tabby</label>
</item>
</items>
</cat>
</instance>
I cannot change the XML structure.
I need to map this to the following class:
[Serializable, XmlRoot("instance")]
public class AnimalInstance
{
public string Dog { get; set; }
public string Cat { get; set; }
}
I'm not really sure where to start on this one without manually parsing the XML. I'd like to keep the code as brief as possible. Any ideas? (and no, my project doesn't actually involve cats and dogs).

A simple working example (skipping the cat for brevity) using System.Xml.Serialization:
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
[XmlRoot("instance")]
public class AnimalInstance {
[XmlElement("dog")]
public Dog Dog { get; set; }
}
public class Dog {
[XmlArray("items")]
[XmlArrayItem("item")]
public List<Item> Items = new List<Item>();
}
public class Item {
[XmlElement("label")]
public string Label { get; set; }
}
class Program {
static void Main(params string[] args) {
string xml = #"<instance>
<dog>
<items>
<item>
<label>Spaniel</label>
</item>
</items>
</dog>
</instance>";
XmlSerializer xmlSerializer = new XmlSerializer(typeof(AnimalInstance));
AnimalInstance instance = (AnimalInstance)xmlSerializer.Deserialize(new StringReader(xml));
}
}

why is it such a problem to write custom parsing code? for your simple example it may actually involve less code to use an XDocument:
XDocument xdoc = XDocument.Parse(xml);
AnimalInstance animal = new AnimalInstance()
{
Dog = xdoc.XPathSelectElement("instance/dog/items/item/label").Value,
Cat = xdoc.XPathSelectElement("instance/cat/items/item/label").Value
};

When in doubt with creating your xml serialization classes, i find the easiest way to solve the problem is to:
dump all your dummy data into an XML file
run xsd.exe to create a .xsd schema file
run xsd.exe on your schema file to create a class file
i wrote a quick tutorial on it in a blog post a while ago:
http://www.diaryofaninja.com/blog/2010/05/07/make-your-xml-stronglytyped-because-you-can-and-its-easy
it takes less than a minute and you can then easily tweak things from there. XSD.exe is your friend

well you may do custom serialization via IXmlSerializable Interface to get structure as you want

You may also transform the XML document using an XSL to a structure you like and deserialize the output of this transformation. But for such an easy structure, you should use another solution like the one provided by Paolo Tedesco.

Related

Deserialize using DataContractSerializer

I am trying to Deserialize an xml file that looks like the following
<?xml version="1.0"?>
<Test xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication6">
<values>
<String>Value 1</String>
<String>Value 2</String>
</values>
</Test>
to an object that is this
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/ConsoleApplication6")]
public class Test
{
[DataMember(Name = "values")]
public String[] values;
}
with
var ds = new DataContractSerializer(typeof(Test));
using (Stream stream1 = File.OpenRead(#"C:\Projects\test1.xml"))
{
Test rr = (Test)ds.ReadObject(stream1);
}
However none of the values are deserializing. I just see and empty array in Test rr. Could you please tell what I am doing wrong. Thanks in advance.
If you need fine control of the XML that is emitted when serializing, you should not use DataContractSerializer. It is has very limited flexibility. You would be better off using XmlSerializer, which has liimtitations as well, but is much more flexible than DataContractSerializer.
That being said, here is how you can do what you want with DataContractSerializer.
Change the default namespace on your xml to use the one that DataContractSerializeruses by default.
<?xml version="1.0"?>
<Test xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/">
<values>
<String>Value 1</String>
<String>Value 2</String>
</values>
</Test>
Instead of using string[] create your own custom type that derives from List<string>. This must be done solely for the purpose of having something to hang CollectionDataContractAttribute on. CollectionDataContractAttribute is what will let you specify the name of the elements inside <values>.
[DataContract]
public class Test
{
[DataMember(Name = "values")]
public TestValues values;
}
[CollectionDataContract(ItemName = "String")]
public class TestValues : List<string> { }
The DataContractSerializer has its own rules for XML and cannot support all XML forms. I suggest using the XmlSerializer.
Use this definition
[XmlClass(Namespace = "http://schemas.datacontract.org/2004/07/ConsoleApplication6")]
public class Test
{
[XmlArray("values")]
[XmlArrayItem("String")]
public String[] values;
}

Xml serialize array of different classes with custom tag names

Suppose I have the following class structure:
[XmlInclude(typeof(CustomNode))]
[XmlInclude(typeof(CustomNode2))]
[XmlRoot("node")]
class Node
{
[XmlElement("node")]
public Node[] Children { get; set; }
}
[XmlRoot("custom-node")]
class CustomNode : Node { }
[XmlRoot("custom-node-2")]
class CustomNode2 : Node { }
The I create the following structure:
var root = new Node { Children = new Node[2] };
root.Children[0] = new CustomNode();
root.Children[1] = new CustomNode2();
When I Xml serialize this structure, I get following output:
<node>
<node xsi:Type="CustomNode"/>
<node xsi:Type="CustomNode2"/>
</node>
But I would like to see (and be able to load properly) something like this:
<node>
<custom-node/>
<custom-node-2/>
</node>
Is it possible at all for XmlSerializer? The whole problem is because I intend to manually create source xml, and am trying to make it more humanreadble and friendly.
I found a solution for your problem which is nearly what you need:
[XmlRoot(ElementName = "node")]
public class Node
{
[XmlArrayItem(typeof(CustomNode), ElementName = "custom-node")]
[XmlArrayItem(typeof(CustomNode2), ElementName = "custom-node-2")]
public List<Node> Children { get; set; }
}
[XmlRoot(ElementName = "custom-node")]
public class CustomNode : Node { }
[XmlRoot(ElementName = "custom-node-2")]
public class CustomNode2 : Node { }
results in:
<node xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Children>
<custom-node />
<custom-node-2 />
</Children>
</node>
Hope that helps a bit.
I don't think you'll be able persuade the XmlSerializer to output this xml if you are only using the various xml attibutes to specify the xml.
The [XmlRoot] attribute you are attempting to use is ignored if the type is not itself at the root (rather than being composed in another class).
However, you can implement the IXmlSerializable interface and provide an implementation for IXmlSerializable.WriteXml, which the XmlSerializer will call if present.
This will allow you to craft the xml you want directly to an XmlWriter.
If you also need to deserialize, you'll need to provide an equivalent implementation for IXmlSerializable.ReadXml as well.

c# deserialize xml with a specified 'encoding' and 'stylesheet' lines

I am creating an XML file using the System.Xml.Serialization module.
I have a class that gets serialized into an XML file. The file looks like this:
<itemList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<itemListed>
<item ID="81288" Synopsys="Reset search point" CompletedTime="7/27/10 4:12 PM" Resolver="owner1" />
<item ID="81285" Synopsys="Added contructor" CompletedTime="6/05/10 9:23 AM" Resolver="owner2" />
</itemListed>
</itemList>
Problem is, I would like it to generate this:
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet type="text/xsl" href="item.xsl"?>
<itemListed>
<item ID="81288" Synopsys="Reset search point" CompletedTime="7/27/10 4:12 PM" Resolver="owner1" />
<item ID="81285" Synopsys="Added contructor" CompletedTime="6/05/10 9:23 AM" Resolver="owner2" />
</itemListed>
Any idea what I need to change to my class?
My code:
public class Item
{
[XmlAttribute("ID")]
public string ID { get; set; }
[XmlAttribute("Synopsys")]
public string Synopsys { get; set; }
[XmlAttribute("CompletedTime")]
public string CompletedTime { get; set; }
[XmlAttribute("Resolver")]
public string Resolver { get; set; }
}
public class ItemList
{
[XmlArray(ElementName = "itemListed")]
[XmlArrayItem(ElementName = "item")]
public List<Item> ItemList { get; set; }
}
I appreciate any help.
Thanks
Tony
I haven't found an easy way to customize the stylesheet or the encoding line but I found a good suggestion here:
link text
The idea is to pretty much write your own serialization class. I took the idea from the article and I created a class that serializes the class (using the C# library) then a filter modifies the header to modify the encoding line and add the stylesheet line.
When I load the xml, I read the file, I pass it through the filter to remove the stylesheet line and I change back the encoding line. Once I have done that, I use the de-serializer provided by C#.
It seems to work.
Tony
One solution might be to implement IXmlSerializable interface on your class. I'm not sure if the XmlWriter will allow you to write a XML tag or not. Proper way to implement IXmlSerializable?

Why does the OnDeserialization not fire for XML Deserialization?

I have a problem which I have been bashing my head against for the better part of three hours. I am almost certain that I've missed something blindingly obvious...
I have a simple XML file:
<?xml version="1.0" encoding="utf-8"?>
<WeightStore xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Records>
<Record actual="150" date="2010-05-01T00:00:00" />
<Record actual="155" date="2010-05-02T00:00:00" />
</Records>
</WeightStore>
I have a simple class structure:
[Serializable]
public class Record
{
[XmlAttribute("actual")] public double weight { get; set; }
[XmlAttribute("date")] public DateTime date { get; set; }
[XmlIgnore] public double trend { get; set; }
}
[Serializable]
[XmlRoot("WeightStore")]
public class SimpleWeightStore
{
[XmlArrayAttribute("Records")]
private List<Record> records = new List<Record>();
public List<Record> Records { get { return records; } }
[OnDeserialized()]
public void OnDeserialized_Method(StreamingContext context)
{
// This code never gets called
Console.WriteLine("OnDeserialized");
}
}
I am using these in both calling code and in the class files:
using System.Xml.Serialization;
using System.Runtime.Serialization;
I have some calling code:
SimpleWeightStore weight_store_reload = new SimpleWeightStore();
TextReader reader = new StringReader(xml);
XmlSerializer deserializer = new XmlSerializer(weight_store.GetType());
weight_store_reload = (SimpleWeightStore)deserializer.Deserialize(reader);
The problem is that I am expecting OnDeserialized_Method to get called, and it isn't.
I suspect it might have something to do with the fact that it's XML deserialization rather than Runtime deserialization, and perhaps I am using the wrong attribute name, but I can't find out what it might be.
Any ideas, folks?
There's no equivalent of OnDeserialized for XML deserialization.
See this post for workarounds: How do you find out when you've been loaded via XML Serialization?
The only way you could do that in a graceful way is to manually implement IXmlSerializable, which is not fun. Simply; XmlSerializer doesn't support serialization callbacks.
Sometimes, though, you can switch to DataContractSerializer, which still offers xml capabilities but which does support serialization callbacks. Unfortunately the xml options are limited - it won't work for you xml structure, since that uses attributes (DataContractSerializer only supports elements).
You might also look at the comments on this answer, which discusses the points from this.

XML Deserialization with C# .NET 3.5

I Have this XML File
<?xml version="1.0" standalone="yes"?>
<Root>
<Object>
<referenceName>People</referenceName>
<query>select * from people</query>
</Object>
<Object>
<referenceName>Countries</referenceName>
<query>select * from countries</query>
</Object>
</Root>
I need to convert into an object with C#.
I got confused how to do it.
Kindly note that I can have alot of objects in the xml file.
I know that i have to use an [XMLArray......
Thanks
The simplest trick here is at the VS command line:
xsd example.xml
xsd example.xsd /classes
Et voila; one example.cs file with example C# that shows how to get that xml from .NET objects via XmlSerializer.
In this case, I expect the following would work:
public class Root
{
[XmlElement("Object")]
public List<SomeOtherObject> Objects { get; set; }
}
public class SomeOtherObject
{
[XmlElement("referenceName")]
public string Name { get; set; }
[XmlElement("query")]
public string Query { get; set; }
}
update: validated it; yup, it works...
XmlSerializer ser = new XmlSerializer(typeof(Root));
using (XmlReader reader = XmlReader.Create(
new StringReader(xml)))
{
var obj = (Root)ser.Deserialize(reader);
// use obj
}
Use the xsd.exe tool to generate an initial set of classes to start with. Once you have those, tweak if needed (post the generated classes) and use System.Xml.Serialization.XmlSerializer to deserialize back into the runtime object.

Categories

Resources