2

I need to deserialize an XML document that looks like this:

<Root>
  <Items>
    <Item>
      <ItemHeader Attr1="A" Attr2="B" Attr3="C" />
      <ItemDetails Attr4="D" Attr5="E" />
    </Item>
    ...
  </Items>
</Root>

Into a class that looks like this:

[Serializable, XmlRoot("Item")]
Public class MyItem
{
  [XmlAttribute("Attr1")]
  public string Attr1 { get; set; }
  [XmlAttribute("Attr5")]
  public string Attr5 { get; set; }
}

And I am using the following code to perform the deserialization:

XDocument doc;
XElement rootElem = doc.Element("Root");

foreach (XElement xe in rootElem.Descendants("Item"))
{
  MyItem item = new MyItem();
  XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyItem));
  XmlReader xRdr = xe.CreateReader();
  item = (MyItem)xmlSerializer.Deserialize(xRdr);
}

However, none of the elements are copied into the object instance.

Is this doable? Do I need to deserialize each sub Element?

Thanks

2 Answers 2

1

I'm not sure there's a way to do that using the default XML serializer via attributes, without doing the whole class structure to match your XML - so ItemHeader and ItemDetails would need their own class.

You can implement the IXmlSerializable interface though, so you can completely customize - if you must keep the structure of MyItem as it is.

static void Main(string[] args)
{
    XmlSerializer myItemSerializer = new XmlSerializer(typeof(MyItem));

    var xmlDoc = XDocument.Parse(@"<Item>
                                <ItemHeader Attr1=""A"" Attr2=""B"" Attr3=""C"" />
                                <ItemDetails Attr4=""D"" Attr5=""E"" />
                            </Item>");

    using (var reader = xmlDoc.CreateReader())
    {
        MyItem myItem = (MyItem)myItemSerializer.Deserialize(reader);
    }


    Console.Read();
}

[Serializable, XmlRoot("Item")]
public class MyItem : IXmlSerializable
{
    [XmlAttribute("Attr1")]
    public string Attr1 { get; set; }
    [XmlAttribute("Attr5")]
    public string Attr5 { get; set; }

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        reader.ReadStartElement("Item");

        do
        {
            switch (reader.Name)
            {
                case "ItemHeader":
                    Attr1 = reader.GetAttribute("Attr1");
                    reader.Read();
                    break;
                case "ItemDetails":
                    Attr5 = reader.GetAttribute("Attr5");
                    reader.Read();
                    break;
                default:
                    throw new XmlException(String.Format("{0} was not expected", reader.Name));
            }
        } while (reader.Name != "Item");


        reader.ReadEndElement();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteStartElement("ItemHeader");
        writer.WriteAttributeString("Attr1", Attr1);
        writer.WriteEndElement();
        writer.WriteStartElement("ItemDetails");
        writer.WriteAttributeString("Attr5", Attr5);
        writer.WriteEndElement();
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

An oddity here, I am seeing that Attr1 gets set correctly and is still there inside of Main but not Attr5. Debugging through, Attr5 is being read and set but doesn't retain its value outside of the MyItem class scope.
That above comment is not entirely true :) My real example is slightly more complex and it is that one that where attribute from the secondary element are not "sticking" though they are being read in.
Sorry, not something I've been able to reproduce with the original XML you posted or what I have above. Note GetAttribute won't fail if not found, it'll just return null - it's also case sensitive. If it's not that i'll need the XML you have.
Mystery solved. I had another Element nested inside of ItemDetails and apparently when the reader hit the closing of ItemDetails, the reader just saw it as ItemDetails and since there were no attributes attached to the close, those attributes were assigned null values.
Ah. I guess reader.ReadOuterXml() instead of reader.Read() might have been a better/safer choice to have avoided that.
|
0

The issue is the xml data you are receiving does not match the serialization attributes of MyItem, schemas differ. There are multiple ways around this but I guess the quickest and dirtiest solution would be to extract the part that interests you:

  XDocument doc = XDocument.Load (@"Your.xml"); 
  XElement rootElem = doc.Element("Root");
  XElement itemsElem = rootElem.Element("Items");
  foreach (XElement xe in itemsElem.Elements("Item"))
  {
    MyItem item = new MyItem()
    {
      Attr1 = xe.Element("ItemHeader").Attribute("Attr1").Value,
      Attr5 = xe.Element("ItemDetails").Attribute("Attr5").Value
    }; 
  }

1 Comment

Thanks for the reply and though a possible solution I'm going with Steve's above as I can keep the internal knowledge of the class and attributes away from the processing code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.