Deserialise XML File to .Net Object - c#

I'm trying to deserialize an xml file to a .NET object by doing something like:
CarCollection myCarCollection = null;
string path = "CarCollection.xml";
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
StreamReader reader = new StreamReader(path);
myCarCollection= (CarCollection)serializer.Deserialize(reader);
reader.Close();
Here is the xml file I'm using:
<?xml version="1.0" encoding="utf-8" ?>
<CarCollection>
<Car ID="A">
<CarType Make="Ford" Model="Focus" />
<CarOwner Name="Tom">
<Report Type="Service">
<ReportList>
<Date>20-08-2010</Date>
</ReportList>
</Report>
</CarOwner>
</Car>
<Car ID="B">
<CarType Make="Vauxhall " Model="Corsa" />
<CarOwner Name="Joe">
<Report Type="Service">
<ReportList>
<Date>10-10-2008</Date>
<Date>10-10-2009</Date>
<Date>10-10-2010</Date>
</ReportList>
</Report>
<Report Type="Accident">
<ReportList>
<Date>20-01-2011</Date>
</ReportList>
</Report>
</CarOwner>
</Car>
</CarCollection>
I've tried many things but can't seem to get it working.
Could anyone please help me how to do deserialize to a .NET object.
Here is the C# Objects
[Serializable()]
[XmlRoot("CarCollection")]
public class CarCollection
{
[XmlArray("Car")]
[XmlArrayItem("Car", typeof(Car))]
public Car[] Cars { get; set; }
}
[Serializable()]
public class Car
{
[XmlAttribute("Make")]
public string CarMakeType { get; set; }
[XmlAttribute("Model")]
public string CarModelType { get; set; }
[XmlArray("CarOwner")]
[XmlArrayItem("CarOwner", typeof(CarOwner))]
public CarOwner[] CarOwners { get; set; }
}
[Serializable()]
public class CarOwner
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlArray("Report")]
[XmlArrayItem("Report", typeof(Report))]
public Report[] Reports { get; set; }
}
[Serializable()]
public class Report
{
[XmlAttribute("Type")]
public string Type { get; set; }
[XmlArray("Report")]
[XmlArrayItem("Report", typeof(DateTime))]
public DateTime[] Reports { get; set; }
}

Tangentially you might find some benefit in using XSD to generate XML from your classes.

I bet this is due to the date format.
the xmlns declaration is also missing.
The Felice suggestion is a good one. Try to produce the desired result with serializing, before trying to deserialize

Read this page in the MSDN to verify your code.
The yellow note halfway down the page specifies what requirements must be met by collections.
Also: pass the Car type too, to the Serializer constructor.
EDIT
The Report and Car tags are not closed!
EDIT
Here is the output when serializing. Spot the differences there are many. The biggest problem is how you are serializing the arrays. Start using plurals (Cars, Owners) for collections that wil make it more readable.
<?xml version="1.0" encoding="utf-8"?>
<CarCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Car>
<Car Make="make">
<CarOwner>
<CarOwner Name="name1">
<Report>
<Report Type="rtype">
<Report>
<Report>2011-01-25T15:22:52.703125+01:00</Report>
</Report>
</Report>
</Report>
</CarOwner>
</CarOwner>
</Car>
<Car Make="make2">
<CarOwner>
<CarOwner Name="name3">
<Report>
<Report Type="rtype">
<Report>
<Report>2011-01-25T15:22:52.703125+01:00</Report>
</Report>
</Report>
</Report>
</CarOwner>
</CarOwner>
</Car>
</Car>

Related

Deserialize XML into simple single array

I need to deserialize XML into one simple array of objects, but can't quite figure out how to do it. Here is my simplified XML:
<?xml version="1.0" encoding="Windows-1250"?>
<document>
<datasets>
<dataset0>
<rows>
<row>
<fields>
<id>1</id>
<name>Cat1</name>
</fields>
</row>
<row>
<fields>
<id>2</id>
<name>Cat2</name>
</fields>
</row>
</rows>
</dataset0>
</datasets>
</document>
I've created class for the object to deserialize into
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
And the deserialization code
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Category[]), new XmlRootAttribute("rows"));
using (FileStream myFileStream = categoryFile.OpenRead())
{
var categoryArray = (Category[])xmlSerializer.Deserialize(myFileStream);
}
This obviously doesn't work, I've tried various XmlElement and XmlRoot tags, but I don't quite know what I'm doing so I stripped all that out to avoid public embarrassment. Thanks for any input.
It seems that, the object which you want to deserialize doesn't have exact compatibility with your xml file. So, in this case it could be more flexible to use Linq to xml solution.
var xDocument = XDocument.Parse(xml);
var categoryList = xDocument.Descendants("fields").Select(x => new Category
{
Name = x.Element("name").Value,
Id = int.Parse(x.Element("id").Value)
});

c# XmlSerializer List of typed objects

I have the problem that I got xml from a REST service and have to deserialize it to an object structure and there is a type="proglang" in it.
<listResult>
<listEntry xsi:type="proglang">
<id>0</id>
<name>C#</name>
</listEntry>
<listEntry xsi:type="proglang">
<id>0</id>
<name>C#</name>
</listEntry>
</listResult>
How should the object model with the xml attributes look like in c#?
My Code so far:
[XmlRoot("listResult")]
public class ListResult
{
[XmlElement("listEntry")]
//[XmlArrayItem(Type=typeof(proglang))]
public List<proglang> listEntry;
}
public class proglang
{
[XmlElement("code")]
public int id;
[XmlElement("label")]
public string name;
}
and the resulting XML from serializing:
<listResult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<listEntry>
<code>1</code>
<label>C#</label>
</listEntry>
<listEntry>
<code>1</code>
<label>C++</label>
</listEntry>
</listResult>

deserializing an xml to c# object and serializing it in a different way

Hi I have a Web Service that feeds my application with the following XML:
<Value><TABLE>
<PRODUCT>
<ProductID> 1 </ProductID>
<Category> sport </Category>
<Description> calcio </Description>
<Name> palla </Name>
<Price> 10 </Price>
</PRODUCT>
<PRODUCT>
<ProductID> 2 </ProductID>
<Category> sport </Category>
<Description> tennis </Description>
<Name> racchetta </Name>
<Price> 100 </Price>
</PRODUCT>
<PRODUCT>
<ProductID> 3 </ProductID>
<Category> sport </Category>
<Description> golf </Description>
<Name> borsa </Name>
<Price> 150 </Price>
</PRODUCT>
</TABLE></Value>
I wrote the following object models and I manage to deserialize correcly using XmlSerializer.
[XmlRoot("Value")]
public class Value {
[XmlElement("TABLE")]
public TABLE TABLE { get; set; }
}
public class TABLE {
[XmlElement("PRODUCT")]
public List<Product> Products { get; set; }
}
public class Product {
public int ProductID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public int Price { get; set; }
}
Now, I eventually add new Product objects and I want to serialize the model into and XML in order to sending it back to the web service. The problem is that the XML structure accepted is slighty different, I'd want the XML structure to be like this:
<setProdotti>
<streams>
<instream>
<Value><TABLE>
<PRODUCT>
<ProductID> 1 </ProductID>
<Category> sport </Category>
<Description> calcio </Description>
<Name> palla </Name>
<Price> 10 </Price>
</PRODUCT>
</TABLE></Value>
</instream>
</streams>
</setProdotti>
Basically the same as the input XML with the exception of the embedding tags setProdotti, streams and instream, which are fixed and known (can be hardcoded).
Can it be done with the current model? I tryed to use XmlSerializer's Serialize method but id output an XML based on the model (of course) and with tags on the root elements that I'd like to avoid too.
<?xml version="1.0" encoding="utf-16"?><Value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TABLE>
<PRODUCT><ProductID>1</ProductID><Name> palla </Name><Description> nike </Description><Category> calcio </Category><Price>10</Price></PRODUCT>
</TABLE>
</Value>
Thanks for your help.
Following the advice of Alexander Petrov, I looked into detail the XmlWriter
The resulting code that satisfy my requirements is:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer xmlserializer = new XmlSerializer(typeof(Value));
StringWriter stringWriter = new StringWriter();
XmlWriter writer2 = XmlWriter.Create(stringWriter, new XmlWriterSettings {
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
});
writer2.WriteStartElement("setProdotti");
writer2.WriteStartElement("streams");
writer2.WriteStartElement("instream");
xmlserializer.Serialize(writer2, p, ns);
writer2.Dispose();
string serializedXml = stringWriter.ToString();
The part about XmlSerializerNamespacesavoid the creation of XML attributes on the Value element (p) and i set up XmlWriterSetting so that I have no XML declaration at the beginning of the string.
Apparently .Dispose() closes the tags I opened with .WriteStartElement. I was looking for the .Close() method but it doesn't exist anymore.

How can I use a class as a data model in querying XDocument?

I have an Xml document:
<?xml version="1.0" encoding="utf-8"?>
<Family xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Person member="father" id="0">
<Surname>Smith</Surname>
<Forename>Robert</Forename>
<Person member="son" id="1">
<Surname>Smith</Surname>
<Forename>Sam</Forename>
<Person member="son" id="2">
<Surname>Smith</Surname>
<Forename>Jeff</Forename>
</Person>
</Person>
<Person member="daughter" id="3">
<Surname>Smith</Surname>
<Forename>Sarah</Forename>
</Person>
</Person>
</Family>
...and a few supporting classes as follows:
[XmlRoot]
public class Family {
[XmlElement]
public List<Person> Person;
}
public class Person {
[XmlAttribute("member")]
public MemberType Member { get; set; }
[XmlAttribute("id")]
public int Id { get; set; }
[XmlElement]
public string Surname { get; set; }
[XmlElement]
public string Forename { get; set; }
[XmlElement("Person")]
public List<Person> People;
}
public enum MemberType {
Father,
Mother,
Son,
Daughter
}
Now, say Family has a method defined as such:
public IEnumerable<Person> Find (Func<Person, bool> predicate) {
// also, I know that this SelectMany will not work - assume this foreach works
// on a flattened list of people
foreach (var p in family.Person.SelectMany()) {
if(predicate(p)) {
yield return p;
}
}
}
...but I don't want to deserialize the Xml to the Family and Person classes. I would like to simply load the XDocument and query that directly - but working with XElement, XAttribute, and XName is not that friendly when providing an API. I realize that I need the classes - Family & Person - but they are simply models.
Can I have a Find method where I can pass something like:
IEnumerable<Person> people = someBusinessObj.Find(p => p.Forename == "Jeff");
Update
I would prefer a solution that does not involve an open-source project (as #MartinHonnen refers).
Why do you not want to deserialize the XML into objects? This will give you exactly the programmatic interface you require. I would:
Deserialize using the XmlSerializer class.
Allow you users to query via Linq-to-Objects.
All this requires very little effort to implement!

Multiple namespaces with XmlSerializer

This is the scenario:
I have nested classes and need to serialize then in an xml document
[XmlRoot(Namespace="http://www.foo.bar/myschema")]
public class root
{
[XmlAttribute]
public string version { get; set; }
[XmlElement]
public child child { get; set; }
...
}
[XmlRoot(Namespace="http://www.foo.bar/myschema")]
public class child
{
[XmlElement]
public int elemA { get; set; }
[XmlElement]
public string elemB { get; set; }
...
}
I have created an method based in another example to remove additional namespaces and set a custom one:
public static void Save<T>(this T type, string path)
{
System.Xml.Serialization.XmlSerializer xs =
new System.Xml.Serialization.XmlSerializer(type.GetType());
System.Xml.Serialization.XmlSerializerNamespaces ns =
new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add("", "http://www.foo.bar/myschema");
using(XmlWriter file = XmlWriter.Create(path))
{
xs.Serialize(file, type, ns);
}
}
And I get this code as result:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://www.foo.bar/myschema" version="2.00">
<child>
<elemA>1</elemA>
...
</child>
</root>
But expected this:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://www.foo.bar/myschema" version="2.00">
<child xmlns="http://www.foo.bar/myschema">
<elemA>1</elemA>
...
</child>
</root>
We must set the custom namespace declaration in both tags. Is this possible?
Edit:
Here is a real-world example:
<?xml version="1.0" encoding="UTF-8"?>
<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.01">
<idLote>200602220000001</idLote>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe31060243816719000108550000000010001234567890" versao="1.01">
...
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
...
</NFe>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe31060243816719000108550000000010011234567900" versao="1.01">
...
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
...
</NFe>
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe31060243816719000108550000000010021234567916" versao="1.01">
...
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
...
</NFe>
</enviNFe>
From an XML point of view your examples are identical, so the first one is perfectly fine. If you have to use the second one, there is a serious problem with our XML understanding or your processing pipeline.

Categories

Resources