XML-Deserialization of a nested List C# - c#

I am trying to deserialize a Xml File using the XmlSerializer.
A part of my file looks like this:
<bla>
<ListOfListOfTest>
<ListOfTest>
<Test>
</Test>
</ListOfTest>
</ListOfListOfTest>
</bla>
I tried different ways but it does not work.
My first try looked like:
public class bla
{
public bla()
{
ListOfListOfTest = new List<List<Test>>();
}
[...]
public List<List<Test>> ListOfListOfTest{ get; set; }
}
-> does not work.
Second try:
public class bla
{
public bla()
{
ListOfListOfTest = new List<List<Test>>();
}
[..]
public List<List<Test>> ListOfListOfTest { get; set; }
[XmlArrayItemAttribute]
public List<List<Test>> listOfListOfTest { get { return ListOfListOfTest ; } }
}
-> failed as well
Third try:
public class bla
{
public bla()
{
ListOfListOfTest = new List<Foo>();
}
[...]
public List<Foo> ListOfListOfTest { get; set; }
}
public class Foo
{
public Foo()
{
ListOfTest = new List<Test>();
}
public List<Test> ListOfTest { get; set; }
}
-> failed...
Failed means that the XmlSerializer does not fill the List during serializer.Deserialize().
I´m always getting a List with zero Elements in it.
What am i doing wrong?
thanks for your effort

To get to grips with how the XML will look when correctly deserialized, copy your XML:
<bla>
<ListOfListOfTest>
<ListOfTest>
<Test>
</Test>
</ListOfTest>
</ListOfListOfTest>
</bla>
Create a class in C#. Click "Edit" at the top, then "Paste Special" then "Paste XML As Classes" while your cursor is inside the class. Visual Studio will correctly deserialize the XML for you and create the necessary classes. Use this to compare what you thought you needed, and what you actually need, in order to clarify for yourself how deserialization should work.

Something like this?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
class Program {
static void Main() {
var xml = #"<bla>
<ListOfListOfTest>
<ListOfTest>
<Test>
</Test>
</ListOfTest>
</ListOfListOfTest>
</bla>";
var bar = (Bar)new XmlSerializer(typeof(Bar)).Deserialize(new StringReader(xml));
Console.WriteLine(bar.Lists.Sum(_ => _.Items.Count)); // 1
}
}
[XmlRoot("bla")]
public class Bar {
[XmlArray("ListOfListOfTest")]
[XmlArrayItem("ListOfTest")]
public List<Foo> Lists { get; } = new List<Foo>();
}
public class Foo {
[XmlElement("Test")]
public List<Test> Items { get; } = new List<Test>();
}
public class Test { }
The actual layout depends on which elements might be duplicated, and whether you need to be able to reproduce the exact organisation (vs just wanting all the Test items). In the code above, ListOfListOfTest is not expected to be duplicated, but there can be any number of ListOfTest or Test elements.

Visual Studio has a handy option - you should just copy-paste your xml and go to menu Edit > Paste Special > Paste XML As Classes. And Visual Studio will generate classes which you can use to serialize/deserialize your xml. In this particular case it will generate:
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class bla
{
private blaListOfListOfTest listOfListOfTestField;
public blaListOfListOfTest ListOfListOfTest
{
get { return this.listOfListOfTestField; }
set { this.listOfListOfTestField = value; }
}
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class blaListOfListOfTest
{
private blaListOfListOfTestListOfTest listOfTestField;
public blaListOfListOfTestListOfTest ListOfTest
{
get { return this.listOfTestField; }
set { this.listOfTestField = value; }
}
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class blaListOfListOfTestListOfTest
{
private object testField;
public object Test
{
get { return this.testField; }
set { this.testField = value; }
}
}
You can do some adjustments after that - e.g. remove type qualifiers or replace properties with auto-properties (which can be done with visual studio extensions). After few keystrokes:
[Serializable]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false)]
public partial class bla
{
public blaListOfListOfTest ListOfListOfTest { get; set; }
}
[Serializable]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class blaListOfListOfTest
{
public blaListOfListOfTestListOfTest ListOfTest { get; set; }
}
[Serializable]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class blaListOfListOfTestListOfTest
{
public object Test { get; set; }
}
And deserialization will look like:
var serializer = new XmlSerializer(typeof(bla));
var blaObj = serializer.Deserialize(new StringReader(xmlString));

Related

How do I name my XML header 'header list-id: "inventory"' in a C# model?

The first step of a project I'm working on is to create an XML file from a list of records. It used to be written in java, but I'm migrating it to C#. The way they did it in java was by explicitly writing out each and every line, and I'm trying to incorporate a way to do it programmatically so the values can be changed during later steps of deployment.
The file that I have to replicate has a header name of header list-id: "inventory".
I have it just writing 'header' fine, but I can't figure out how to include the rest.
[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = false, Namespace = "http://www.demandware.com/xml/impex/inventory/2007-05-31")]
[XmlRoot(Namespace = "http://www.demandware.com/xml/impex/inventory/2007-05-31", IsNullable = false)]
public partial class inventory
{
private inventoryInventorylist inventorylistField;
[XmlElement("inventory-list")]
public inventoryInventorylist inventorylist
{
get
{
return inventorylistField;
}
set
{
inventorylistField = value;
}
}
}
[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true, TypeName = "inventory-list")]
public partial class inventoryInventorylist
{
public inventoryInventorylistHeader header;
private inventoryInventorylistHeader headerField
{
get
{
return header;
}
set
{
header = value;
}
}
}
[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = false, TypeName = "header")]
public partial class inventoryInventorylistHeader
{
[XmlElement("default-instock")]
public bool defaultinstock
{
get
{
return defaultinstockField;
}
set
{
defaultinstockField = false;
}
}
[XmlElement("description")]
public string description
{
get
{
return descriptionField;
}
set
{
descriptionField = "Inventory";
}
}
[XmlElement("use-bundle-inventory-only")]
public bool usebundleinventoryonly
{
get
{
return usebundleinventoryonlyField;
}
set
{
usebundleinventoryonlyField = false;
}
}
[XmlElement("on-order")]
public bool onorder
{
get
{
return onorderField;
}
set
{
onorderField = false;
}
}
}
Creation:
var inventory = new inventory()
{
inventorylist = new inventoryInventorylist()
{
header = new inventoryInventorylistHeader()
{
defaultinstock = false,
description = "Inventory",
usebundleinventoryonly = false,
onorder = false
}
}
}
Returns this:
<inventory xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.demandware.com/xml/impex/inventory/2007-05-31">
<inventory-list>
<header>
<default-instock>false</default-instock>
<description>Inventory</description>
<use-bundle-inventory-only>false</use-bundle-inventory-only>
<on-order>false</on-order>
</header>
I want it to return this:
<inventory xmlns="http://www.demandware.com/xml/impex/inventory/2007-05-31">
<inventory-list>
<header list-id="inventory">
<default-instock>false</default-instock>
<description>Inventory</description>
<use-bundle-inventory-only>false</use-bundle-inventory-only>
<on-order>false</on-order>
</header>
If I write this in the model:
public partial class inventoryInventorylist
{
[XmlElement("header list-id:\"inventory\"")]
public inventoryInventorylistHeader header;
Then it gets changed to this:
<inventory-list>
<header_x0020_list-id_x003A__x0022_inventory_x0022_>
<default-instock>false</default-instock>
<description>Inventory</description>
<use-bundle-inventory-only>false</use-bundle-inventory-only>
<on-order>false</on-order>
</header_x0020_list-id_x003A__x0022_inventory_x0022_>
How would I force it to display the desired text when I serialize the model?
You could use a XmlAttribute attribute for that.
If you add
[XmlAttribute("list-id")]
public string listid
{
get;
set;
}
to your inventoryInventorylistHeader class, you will get the xml as described in your question.
What i usually do is take the xml and generate an xsd using visual studio.
Then I will open a vs developer command prompt and use xsd.exe to generate the classes. These can then be slightly refactored to look nicer in code.

C# - XML serialization: omitting a certain element in my XML output

I have a weird XML setup here: I need to interface with a third-party and their XML layout that they're using is beyond my control - no chance to changing anything...
In the scope of a larger XML, I find myself needing to serialize (and later also deserialize) a list of items (for a mobile device) which are defined as having a Type and a Number property (both strings). So this would be something like:
public class SerialNumber
{
public string Type { get; set; }
public string Number { get; set; }
}
And this would normally serialize a List<SerialNumber> SerialNumbers as
<SerialNumbers>
<SerialNumber>
<Type>SN</Type>
<Number>CBS583ABC123</Number>
</SerialNumber>
<SerialNumber>
<Type>IMEI</Type>
<Number>35-924106-659945-4</Number>
</SerialNumber>
</SerialNumbers>
However, in my case, I need to send this XML:
<SerialNumbers>
<Type>SN</Type>
<Number>CBS583ABC123</Number>
<Type>IMEI</Type>
<Number>35-924106-659945-4</Number>
</SerialNumbers>
So basically, the list elements need to be omitted - I just need a container <SerialNumbers> and then for each entry in the list, I only need to serialize out the Type and Number subelements.
How can I do this easily in .NET with the XmlSerializer ?
I tried to use
[XmlRoot(ElementName="")]
public class SerialNumber
or
[XmlArray]
[XmlArrayItem(ElementName = "")]
public List<SerialNumber> SerialNumbers { get; set; }
but neither of these worked - I still get my full serialization with the <SerialNumber> elements inside the <SerialNumbers> container...
Is there an easy trick to achieve what I'm looking for? I'd much rather not go low-level and start concetanating together my XML manually....
Thanks!
You could use custom serialization with IXmlSerializable.
public class SerialNumbers : List<SerialNumber>, IXmlSerializable
{
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
Clear();
reader.ReadStartElement();
while (reader.NodeType != XmlNodeType.EndElement)
{
var serialNumber = new SerialNumber
{
Type = reader.ReadElementContentAsString("Type", ""),
Number = reader.ReadElementContentAsString("Number", "")
};
Add(serialNumber);
}
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
foreach (var serialNumber in this)
{
writer.WriteElementString("Type", serialNumber.Type);
writer.WriteElementString("Number", serialNumber.Number);
}
}
}
public class SerialNumber
{
public string Type { get; set; }
public string Number { get; set; }
}
Example:
var ser = new XmlSerializer(typeof(SerialNumbers));
var reader = new StringReader(#"
<SerialNumbers>
<Type>SN</Type>
<Number>CBS583ABC123</Number>
<Type>IMEI</Type>
<Number>35-924106-659945-4</Number>
</SerialNumbers>
".Trim());
var result = (SerialNumbers) ser.Deserialize(reader);
var writer = new StringWriter();
ser.Serialize(writer, result);
Result:
<?xml version="1.0" encoding="utf-16"?>
<SerialNumbers>
<Type>SN</Type>
<Number>CBS583ABC123</Number>
<Type>IMEI</Type>
<Number>35-924106-659945-4</Number>
</SerialNumbers>
This might do the trick for you
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class SerialNumbers
{
private string[] itemsField;
private ItemsChoiceType[] itemsElementNameField;
[System.Xml.Serialization.XmlElementAttribute("Number", typeof(string))]
[System.Xml.Serialization.XmlElementAttribute("Type", typeof(string))]
[System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemsElementName")]
public string[] Items
{
get
{
return this.itemsField;
}
set
{
this.itemsField = value;
}
}
[System.Xml.Serialization.XmlElementAttribute("ItemsElementName")]
[System.Xml.Serialization.XmlIgnoreAttribute()]
public ItemsChoiceType[] ItemsElementName
{
get
{
return this.itemsElementNameField;
}
set
{
this.itemsElementNameField = value;
}
}
}
[System.Xml.Serialization.XmlTypeAttribute(IncludeInSchema = false)]
public enum ItemsChoiceType
{
Number,
Type,
}

Deserialising XML to nested object List returning count 0

I am having some issue with deserialising some XML to an object list, always getting count 0 and within that "raw data" when inspecting DateAndTimeSlot during debug.
Unfortunately I cannot change the names of these elements.
However when checking the XML I get back, there are DateAndTimeslot objects in the XML.
With other object lists I have all seems fine, without the inclusion of namespaces.
What have I missed?
C# Code:
[XmlRoot("AppointmentAvailabilityStatusResponse")]
public class CheckAppointmentAvailabilityContainer
{
[XmlElement("AppointmentAvailabilityStatusResult")]
public AppointmentAvailabilityStatus appointmentAvailabilityStatus { get; set; }
}
[XmlRoot("AppointmentAvailabilityStatusResult", Namespace = "Appointments")]
public class AppointmentAvailabilityStatus
{
[XmlArray("DateAndTimeSlot", Namespace = "Appointments")]
[XmlArrayItem(typeof(DateAndTimeslot))]
public DateAndTimeSlots DateAndTimeSlot { get; set; }
[XmlElement("RequestedStatus")]
public int RequestedStatus { get; set; }
}
[XmlRoot(ElementName = "DateAndTimeSlot")]
[XmlType("a")]
public class DateAndTimeSlots : List<DateAndTimeslot> { }
[XmlRoot(ElementName = "DateAndTimeslot", Namespace = "Appointments.TO")]
[XmlType("b")] // if included this renames the node to "b" for some reason
public class DateAndTimeslot
{
[XmlElement("Date")]
public string Date { get; set; }
[XmlElement("TimeSlot")]
public string TimeSlot { get; set; }
}
Shortened XML returned that I wish to fully deserialise.
<AppointmentAvailabilityStatusResponse>
<AppointmentAvailabilityStatusResult xmlns:a="Appointments" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:DateAndTimeSlot xmlns:b="Appointments.TO">
<b:DateAndTimeslot>
<b:Date>14/07/2016</b:Date>
<b:TimeSlot>AM</b:TimeSlot>
</b:DateAndTimeslot>
<b:DateAndTimeslot>
<b:Date>14/07/2016</b:Date>
<b:TimeSlot>PM</b:TimeSlot>
</b:DateAndTimeslot>
</a:DateAndTimeSlot>
<a:RequestStatus>0</a:RequestStatus>
</AppointmentAvailabilityStatusResult>
</AppointmentAvailabilityStatusResponse>
XML if I serialise a dummy object - some differences which I'm trying to rectify, not sure if the namespaces are necessary for deserialisation though
<AppointmentAvailabilityStatusResponse>
<AppointmentAvailabilityStatusResult>
<DateAndTimeSlot xmlns=\"Appointments\">
<DateAndTimeslot>
<Date xmlns=\"Appointments.TO\">today</Date>
<TimeSlot xmlns=\"Appointments.TO\">now</TimeSlot>
</DateAndTimeslot>
</DateAndTimeSlot>
<RequestedStatus xmlns=\"Appointments\">0</RequestedStatus>
</AppointmentAvailabilityStatusResult>
</AppointmentAvailabilityStatusResponse>
Deserialiser
public static T DeserializeThis<T>(string cleanXml)
{
//string cleanXml = RemoveBom(dirtyXml);
bool check = cleanXml.TrimStart().StartsWith("<");
if (!string.IsNullOrEmpty(cleanXml) && cleanXml.TrimStart().StartsWith("<"))
{
try
{
XmlSerializer xs = new XmlSerializer(typeof(T));
MatchCollection mc = Regex.Matches(cleanXml, #"</?(\d\w+)");
List<string> elements = new List<string>();
foreach (Match m in mc)
{
string cpval = m.Groups[1].Value;
if (!elements.Contains(cpval)) { elements.Add(cpval); }
}
foreach (string e in elements)
{
cleanXml = cleanXml.Replace(e, "d_" + e);
}
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(cleanXml)))
{
using (StringReader sr = new StringReader(cleanXml))
{
return (T)xs.Deserialize(sr);
}
}
}
catch(XmlException x)
{
var obj = (T)Activator.CreateInstance(typeof(T));
Type type = obj.GetType();
return (T)obj;
}
}
else
{
var obj = (T)Activator.CreateInstance(typeof(T));
Type type = obj.GetType();
// add in the generic derived class property search and assign
return (T)obj;
}
}
Thank you to those who commented above - I finally got it working. Removing the XmlArray and not including Anonymous and IsNullable attributes seemed to be the issue although I am unsure why as it works with all of the other functions I have, serializable possibly doesn't need to be present either.
Working class structure minus the container as that didn't change:
[Serializable()]
[XmlType(AnonymousType = true, Namespace = "")]
public class AppointmentAvailabilityStatusResult : WebserviceMessage
{
[XmlElement("DateAndTimeSlot", Namespace = "Appointments")]
public DateAndTimeSlot DateAndTimeSlot { get; set; }
[XmlElement("RequestedStatus")]
public int RequestedStatus { get; set; }
}
[Serializable()]
[XmlType(AnonymousType = true, Namespace = "Appointments")]
[XmlRoot(ElementName = "DateAndTimeSlot",Namespace = "Appointments", IsNullable = false)]
public class DateAndTimeSlot
{
[XmlElement(ElementName = "DateAndTimeslot", Namespace = "Appointments.TO")]
public List<DateAndTimeslot> DateAndTimeslot { get; set; }
}
[Serializable()]
[XmlType(AnonymousType = true, Namespace = "Appointments.TO")]
[XmlRoot(Namespace = "Appointments.TO", IsNullable = false)]
public class DateAndTimeslot
{
[XmlElement("Date")]
public string Date { get; set; }
[XmlElement("TimeSlot")]
public string TimeSlot { get; set; }
}

NULL-Values when deserializing XML

I try to deserialize different XML docs which are build according to the same schema and yes, I'm a newby to this topic. I managed to retrieve some auto-generated Code from using xsd-files as “type definition language” (VS2013) and for me it looks ok and basically it’s working which means I can start the program. However, there is no output of my program and by debugging it I can see that the most important fields are not populated. Here is the situation (the original generated code and the XML are of course much longer but I cut to the classes which produce the problem):
XML:
<?xml version="1.0" encoding="UTF-8"?>
<mainDatas
xmlns="http://www.myNamespace.com/"
xmlns:ons="http://www.myOtherNamespace.com/"
xmlns:aan="http://www.andAnotherNamespace.com/">
<contains>
<ons:FeatureCollection aan:id="UHL">
<aan:boundedBy>999.9</aan:boundedBy>
<aan:featureMember>
<myClass1 aan:id="XXX0001">
</myClass1>
</aan:featureMember>
<myClass2 aan:id="XXX0002">
</myClass2>
</aan:featureMember>
<myClass1 aan:id="XXX0003">
</myClass1>
</aan:featureMember>
</ons:FeatureCollection>
</contains>
</mainDatas>
C# (auto-generated):
[System.CodeDom.Compiler.GeneratedCodeAttribute("MSBuild", "12.0.30723.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.myNamespace.com/")]
[System.Xml.Serialization.XmlRootAttribute("mainDatas", Namespace="http://www.myNamespace.com/", IsNullable=false)]
public partial class mainDatas
{
private mainDatasContains containField;
public mainDatasContains contain
{
get
{
return this.containField;
}
set
{
this.containField = value;
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("MSBuild", "12.0.30723.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.myNamespace.com/")]
public partial class mainDatasContains
{
private FeatureCollectionType1 featureCollectionField;
[System.Xml.Serialization.XmlElementAttribute(Namespace="http://www.myOtherNamespace.com/")]
public FeatureCollectionType1 FeatureCollection
{
get
{
return this.featureCollectionField;
}
set
{
this.featureCollectionField = value;
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("MSBuild", "12.0.30723.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(TypeName="FeatureCollectionType", Namespace="http://www.myOtherNamespace.de/")]
[System.Xml.Serialization.XmlRootAttribute("FeatureCollection", Namespace="http://www.myOtherNamespace.de/", IsNullable=false)]
public partial class FeatureCollectionType1
{
private string lockIdField;
private FeaturePropertyType[] featureMemberField;
[System.Xml.Serialization.XmlAttributeAttribute()]
public string lockId
{
get
{
return this.lockIdField;
}
set
{
this.lockIdField = value;
}
}
[System.Xml.Serialization.XmlElementAttribute("featureMember")]
public FeaturePropertyType[] featureMember
{
get
{
return this.featureMemberField;
}
set
{
this.featureMemberField = value;
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("MSBuild", "12.0.30723.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.andAnotherNamespace.com/")]
[System.Xml.Serialization.XmlRootAttribute("featureMember", Namespace="http://www.andAnotherNamespace.com/", IsNullable=false)]
public partial class FeaturePropertyType
{
private AbstractFeatureType itemField;
private bool ownsField;
public FeaturePropertyType()
{
this.ownsField = false;
}
[System.Xml.Serialization.XmlElementAttribute("DynamicFeature", typeof(DynamicFeatureType))]
[System.Xml.Serialization.XmlElementAttribute("FeatureCollection", typeof(FeatureCollectionType))]
[System.Xml.Serialization.XmlElementAttribute("Observation", typeof(ObservationType))]
public AbstractFeatureType Item
{
get
{
return this.itemField;
}
set
{
this.itemField = value;
}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(false)]
public bool owns
{
get
{
return this.ownsField;
}
set
{
this.ownsField = value;
}
}
}
[System.Xml.Serialization.XmlIncludeAttribute(typeof(myClass1))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(myClass2))]
[System.CodeDom.Compiler.GeneratedCodeAttribute("MSBuild", "12.0.30723.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.andAnotherNamespace.com/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://www.andAnotherNamespace.com/", IsNullable=true)]
public abstract partial class AbstractFeatureType
{
private BoundingShapeType boundedByField;
private LocationPropertyType itemField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public BoundingShapeType boundedBy
{
get
{
return this.boundedByField;
}
set
{
this.boundedByField = value;
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("MSBuild", "12.0.30723.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://myNamespace.com")]
[System.Xml.Serialization.XmlRootAttribute("myClass1", Namespace="http://www.myNamespace.com/", IsNullable=false)]
public partial class myClass1 : AbstractFeatureType
{
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("MSBuild", "12.0.30723.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://myNamespace.com")]
[System.Xml.Serialization.XmlRootAttribute("myClass2", Namespace="http://www.myNamespace.com/", IsNullable=false)]
public partial class myClass2 : AbstractFeatureType
{
}
Sorry for posting that much auto-generated code. I tried to omitt as much as possible but since I'm quite clueless I'm not sure what is necessary and what not.
And now my piece of work:
XmlSerializer serializer = new XmlSerializer(typeof(mainDatas));
XmlTextReader reader = new XmlTextReader(#".\myData.xml");
mainDatas dataObject = (mainDatas)serializer.Deserialize(reader);
FeaturePropertyType[] allObjects = dataObject.contain.FeatureCollection.featureMember;
foreach(FeaturePropertyType feature in allObjects)
{
if (feature.Item != null && feature.Item.GetType() == typeof(myClass1))
{
this.doSomething((myClass1)feature.Item));
}
else
{
this.doSomethingElse();
}
}
While debugging it shows me that allObjects contains 3 items of type FeaturePropertyType (i.e. the number of myClass1 and myClass2 in the xml-sheet as suspected) but the property Item of these items is null.
When I searched in the Internet many people suggested that it might be a problem with the namespaces. So I experimented a little bit with it changing it back and forth but without success. And I don't think it's a good idea to change to much in the auto-generated code, anyways.
I would be glad if anyone could give me some suggestions about this issue.
Greetings
TM
EDIT:
I had to edit my code because I forgot to write the inheritance of AbstractFeatureType by myClass1 and myClass2.
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
MainDatas mainDatas = new MainDatas() {
contains = new MainDatasContains() {
featureCollection = new FeatureCollection(){
id = "UHL",
boundedBy = 999.9,
featureMembers = new List<FeatureMember>() {
new FeatureMember() {
myClass1 = new MyClass1() {
id = "XXX0001"
}
},
new FeatureMember() {
myClass2 = new MyClass2() {
id = "XXX0001"
}
},
new FeatureMember() {
myClass1 = new MyClass1() {
id = "XXX0003"
}
}
}
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(MainDatas));
StreamWriter writer = new StreamWriter(FILENAME);
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("aan", "http://www.andAnotherNamespace.com/");
ns.Add("ons", "http://www.myOtherNamespace.com/");
ns.Add("", "http://www.myNamespace.com/");
serializer.Serialize(writer, mainDatas, ns);
writer.Flush();
writer.Close();
writer.Dispose();
XmlSerializer xs = new XmlSerializer(typeof(MainDatas));
XmlTextReader reader = new XmlTextReader(FILENAME);
MainDatas newMainDatas = (MainDatas)xs.Deserialize(reader);
}
}
[XmlRoot("mainDatas", Namespace = "http://www.myNamespace.com/")]
public class MainDatas
{
[XmlElement("contains")]
public MainDatasContains contains { get; set; }
}
[XmlRoot("contains")]
public class MainDatasContains
{
[XmlElement("FeatureCollection", Namespace = "http://www.myOtherNamespace.com/")]
public FeatureCollection featureCollection { get; set; }
}
[XmlRoot(ElementName = "FeatureCollection")]
public class FeatureCollection
{
[XmlAttribute("id", Namespace = "http://www.andAnotherNamespace.com/")]
public string id { get; set; }
[XmlElement("boundedBy", Namespace = "http://www.andAnotherNamespace.com/")]
public double boundedBy { get; set; }
[XmlElement("featureMember", Namespace = "http://www.andAnotherNamespace.com/")]
public List<FeatureMember> featureMembers { get; set; }
}
[XmlRoot("featureMember")]
public class FeatureMember
{
[XmlElement("myClass1")]
public MyClass1 myClass1 { get; set; }
[XmlElement("myClass2")]
public MyClass2 myClass2 { get; set; }
}
[XmlRoot("myClass1")]
public class MyClass1
{
[XmlAttribute("id", Namespace = "http://www.andAnotherNamespace.com/")]
public string id { get; set; }
}
[XmlRoot("myClass2")]
public class MyClass2
{
[XmlAttribute("id")]
public string id { get; set; }
}
}
​

XmlSerializer doesn't fill in values

I created classes from a DTD (over XSD and xsd.exe) for my C# project, so I could easily deserialize them into a model class in my code.
This is roughly how I do it:
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.DtdProcessing = DtdProcessing.Parse;
XmlReader reader = XmlReader.Create(tipsfile.FullName, readerSettings);
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "Tips";
xRoot.IsNullable = true;
XmlSerializer serializer = new XmlSerializer(typeof(Tips), xRoot);
Tips tips = (Tips)serializer.Deserialize(reader);
reader.Close();
But, upon inspection of tips, I see that it holds no values at all from the original XML file. Also, I tried setting a breakpoint on the set-Body of a property of Tips, and it is never reached, although I know for sure that it has a value in the original XML file.
Why is the file not correctly deserialized into the class? Is something missing in my code?
Edit: Here is the Tips.cs file, which was auto-generated from the XSD
using System.Xml.Serialization;
namespace MyNs.Model
{
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://tempuri.org/caravan_1")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://tempuri.org/caravan_1", IsNullable = false)]
public partial class Tips
{
private Chapter[] chapterField;
[System.Xml.Serialization.XmlElementAttribute("Chapter")]
public Chapter[] Chapter
{
get
{
return this.chapterField;
}
set
{
this.chapterField = value;
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://tempuri.org/caravan_1")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://tempuri.org/caravan_1", IsNullable = false)]
public partial class Chapter
{
private string headingField;
private CarType[] carTypesField;
private Section[] sectionField;
private string countryField;
private string languageField;
public string Heading
{
get
{
return this.headingField;
}
set
{
this.headingField = value;
}
}
[System.Xml.Serialization.XmlArrayItemAttribute("CarType", IsNullable = false)]
public CarType[] CarTypes
{
get
{
return this.carTypesField;
}
set
{
this.carTypesField = value;
}
}
[System.Xml.Serialization.XmlElementAttribute("Section")]
public Section[] Section
{
get
{
return this.sectionField;
}
set
{
this.sectionField = value;
}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Country
{
get
{
return this.countryField;
}
set
{
this.countryField = value;
}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Language
{
get
{
return this.languageField;
}
set
{
this.languageField = value;
}
}
}
[... and so on ...]
And a sample XML file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Tips SYSTEM "caravan_1.dtd">
<Tips>
<Chapter Country="dafghagt" Language="de">
<Heading>fgagasgargaergarg</Heading>
<Section id="1">
<Heading>afdhwr6u5taehtaqh5</Heading>
<SubSection id="1">
<Heading>46k46kw6jhadfgadfha</Heading>
<Table>
<Row id="1">
<Heading>sgjsfgjsgfh443q572q356</Heading>
<Items>
<Item car="motor1" id="1">
<BodyText color="red">130*</BodyText>
<Subscript>3</Subscript>
</Item>
</Items>
</Row>
</Table>
</SubSection>
</Section>
</Chapter>
</Tips>
StreamReader sw = new StreamReader(fileName, false);
XmlTextReader xmlw = new XmlTextReader(sw);
XmlSerializer writer = new XmlSerializer(
type,
GetOverrides(),
extraTypes,
null,
null);
object o = writer.Deserialize(xmlw);
As per my comment, get overrides is just a method that contains something similar to your xml root attribute, I can show you this too if necessary
EDIT:
an example of extra types
public Type[] GetTypes()
{
return new Type[] { typeof(Class1), typeof(Class2)};
}
EDIT2: get overrides returns (Note this is untested)
XmlAttributes attribs = new XmlAttributes();
//attribs.XmlRoot - can edit root here (instead of xmlrootattribute)
attribs.XmlElements.Add(myAttribute);
XmlAttributeOverrides myOverride = new XmlAttributeOverrides();
myOverride.Add(typeof(Tips), "Tips", attribs);
return myOverride
Your object may not be identical to the xml model. In that case, you need map the properties of your class to the xml fields. I am giving you a quick example I had in one of my projects which may give you bit more information.
namespace DatabaseModel
{
[Description("Represents the selected nodes in the Coverage pane")]
[Serializable()]
[XmlRootAttribute("XmlCoverage", Namespace = "GISManager", IsNullable = false)]
public class TXmlCoverage : IXmlPolygon
{
[XmlArray(ElementName = "sbets"), XmlArrayItem(ElementName = "sbet")]
public List SbetsSelected { get; set; }
[XmlArray(ElementName = "sdcs"), XmlArrayItem(ElementName = "sdc")]
public List SdcsSelected { get; set; }
[XmlElementAttribute(ElementName = "area")]
public Boolean IsAreaSelected { get; set; }
[XmlElementAttribute(ElementName = "fpath")]
public Boolean IsFlightPathSelected { get; set; }
[XmlElementAttribute(ElementName = "fpoly")]
public Boolean IsFlightPolySelected { get; set; }
[XmlElementAttribute(ElementName = "mpoly")]
public Boolean IsMinePolySelected { get; set; }
[XmlElementAttribute(ElementName = "bldg")]
public Boolean IsBuildingsSelected { get; set; }
[XmlElementAttribute(ElementName = "hgt")]
public Boolean IsHeightSelected { get; set; }
[XmlIgnore()]
public Boolean ArePolygonsSelected { get { return IsMinePolySelected && IsBuildingsSelected && IsHeightSelected; } }
public TXmlCoverage()
{
SbetsSelected = new List<String>();
SdcsSelected = new List<String>();
IsAreaSelected = false;
IsFlightPathSelected = false;
IsFlightPolySelected = false;
}
}
}
So it turns out the problem is namespaces. Because my XML has no root namespace:
<Tips>
but the definition of my Model contained one:
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://tempuri.org/caravan_1", IsNullable = false)]
the Serializer didn't match any elements together. When removing the namespace attribute, it worked fine.
So now, I reworked the complete model and excluded any attributes that were not absolutely necessary (as it turns out, all but one were), so now every Property has just one Attribute: XmlElement and consorts.
using System.Xml.Serialization;
namespace MyNs.Model
{
[XmlRoot("Tips")]
public partial class Tips
{
[XmlElement("Chapter")]
public Chapter[] Chapter { get; set; }
}
[XmlRoot("Chapter")]
public partial class Chapter
{
[XmlElement("Heading")]
public string Heading { get; set; }
[XmlElement("CarType")]
public CarType[] CarTypes { get; set; }
[XmlElement("Section")]
public Section[] Section { get; set; }
[XmlAttribute("Country")]
public string Country { get; set; }
[XmlAttribute("Language")]
public string Language { get; set; }
}
[... and so on ...]
With a simple deserialization it works just fine now:
XmlReaderSettings readerSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse };
XmlSerializer serializer = new XmlSerializer(typeof(Tips));
using (XmlReader reader = XmlReader.Create(fromXmlFile.FullName, readerSettings))
{
Tips tips = (Tips)serializer.Deserialize(reader);
return tips;
}

Categories