1

I have following xml which I want to Deserialize to an object.

<result>
    <reporttype>2</reporttype>
    <items>
        <item>
            <sku>0B0005</sku>
            <style>0B0005.DAK.GREY</style>
            <reason>Barcode cannot be moved to different SKUs</reason>
        </item>
        <item>
            <sku>0B0006</sku>
            <style>0B0006.DAK.GREY</style>
            <reason>Barcode cannot be moved to different SKUs</reason>
        </item>
    </items>
</result>

But following code does not populate the item list, Can someone point me out what I am doing wrong here

string inputString = @"<result><reporttype>2</reporttype><items><item><sku>0B0005</sku><style>0B0005.DAK.GREY</style><reason>Barcode cannot be moved to different SKUs</reason></item><item><sku>0B0005</sku><style>0B0005.DAK.GREY</style><reason>Barcode cannot be moved to different SKUs</reason></item></items></result>";

XmlDocument doc = new XmlDocument();
doc.LoadXml(inputString);

XmlSerializer serializer = new XmlSerializer(typeof(Result));
StringReader rdr = new StringReader(doc.InnerXml);
Result resultingMessage = (Result)serializer.Deserialize(rdr);

public enum ReportType {
    [XmlEnum("0")]
    InternalErrorReport,
    [XmlEnum("1")]
    ErrorReport,
    [XmlEnum("2")]
    InternalSuccessReport
}

[XmlRoot(ElementName = "result")]
public class Result {
    [XmlElement(ElementName = "reporttype")]
    public ReportType reportType { get; set; }
    [XmlElement(ElementName = "items")]
    public List<Item> items = new List<Item>();

    public string error { get; set; }

    public class Item {
        [XmlElement(ElementName = "sku")]
        string sku { get; set; }
        [XmlElement(ElementName = "style")]
        string style { get; set; }
        [XmlElement(ElementName = "reason")]
        string reason { get; set; }
    }
}

Or is there a better way to do this?

4
  • You haven't added the XmlElement attribute to the Item class, so the serializer doesn't know that <item> element is an Item class instance. Commented Jul 11, 2017 at 6:18
  • Thanks for the quick response, I did try, but we cannot use [XmlElement(ElementName = "item")] for classes, it is only valid on property, field, ... So we cannot use that Commented Jul 11, 2017 at 6:25
  • 1
    Change it to [XmlArray("items")] and [XmlArrayItem("items")]. Commented Jul 11, 2017 at 6:29
  • @Fabio That's what I meant to say, thanks! Commented Jul 11, 2017 at 6:30

4 Answers 4

5

You can add two attributes for items property - to satisfy serialization

[XmlRoot(ElementName = "result")]
public class Result 
{
    [XmlArray("items")]
    [XmlArrayItem("item")]
    public List<Item> items = new List<Item>();
}

Or just set type attribute(XmlType) for Item class.
Then will be enough to use only XmlArray attribute for Result.items property. Or not using any attribute at all because name of the property match with name of the element in xml.

[XmlType("item")]
public class Item 
{
    [XmlElement(ElementName = "sku")]
    public string sku { get; set; }
    [XmlElement(ElementName = "style")]
    public string style { get; set; }
    [XmlElement(ElementName = "reason")]
    public string reason { get; set; }
}

And of course make properties public

Sign up to request clarification or add additional context in comments.

Comments

2

Variable need to be public.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            string xml = File.ReadAllText(FILENAME);

            XmlSerializer serializer = new XmlSerializer(typeof(Result));
            StringReader rdr = new StringReader(xml);
            Result resultingMessage = (Result)serializer.Deserialize(rdr);


        }
    }
    public enum ReportType
    {
        [XmlEnum("0")]
        InternalErrorReport,
        [XmlEnum("1")]
        ErrorReport,
        [XmlEnum("2")]
        InternalSuccessReport
    }

    [XmlRoot(ElementName = "result")]
    public class Result
    {
        [XmlElement(ElementName = "reporttype")]
        public ReportType reportType { get; set; }
        public Items items { get; set; }
        public string error { get; set; }


    }
    [XmlRoot("items")]
    public class Items
    {
        [XmlElement(ElementName = "item")]
        public List<Item> items = new List<Item>();
    }
    [XmlRoot("item")]
    public class Item
    {
        [XmlElement(ElementName = "sku")]
        public string sku { get; set; }
        [XmlElement(ElementName = "style")]
        public string style { get; set; }
        [XmlElement(ElementName = "reason")]
        public string reason { get; set; }
    }
}

2 Comments

You don't need create extra class for the collections - serializer perfectly will deserialize it to List<T>
Doing it your way would require adding a label indicating that item is an array. I think this is confusing and explicitly indicating the classes is easier for novices to understand.
1

As explained here you need to mark the list as XmlArray specifing also the XmlArrayItem: Deserializing nested lists with XmlSerializer

So the code becomes:

        string inputString = @"<result><error>error test</error><reporttype>2</reporttype><items><item><sku>0B0005</sku><style>0B0005.DAK.GREY</style><reason>Barcode cannot be moved to different SKUs</reason></item><item><sku>0B0005</sku><style>0B0005.DAK.GREY</style><reason>Barcode cannot be moved to different SKUs</reason></item></items></result>";

        XmlDocument doc = new XmlDocument();
        doc.LoadXml(inputString);

        XmlSerializer serializer = new XmlSerializer(typeof(Result));
        object resultingMessage = null;
        using (StringReader rdr = new StringReader(doc.InnerXml)) {
            resultingMessage = (Result)serializer.Deserialize(rdr);
        }

and the classes:

public enum ReportType {
    [XmlEnum("0")]
    InternalErrorReport,
    [XmlEnum("1")]
    ErrorReport,
    [XmlEnum("2")]
    InternalSuccessReport
}

[XmlRoot(ElementName = "result")]
public class Result {
    [XmlElement(ElementName = "reporttype")]
    public ReportType reporttype { get; set; }
    [XmlArray("items")]
    [XmlArrayItem("item")]
    public List<Item> items { get; set; }
    [XmlElement(ElementName = "error")]
    public string error { get; set; }

    [XmlRoot(ElementName = "items\\item")]
    public class Item {
        [XmlElement(ElementName = "sku")]
        public string sku { get; set; }
        [XmlElement(ElementName = "style")]
        public string style { get; set; }
        [XmlElement(ElementName = "reason")]
        public string reason { get; set; }
    }

}

Please, note also that I encapsulated the string reader in a using block, in order to dispose the object once the read ends.

Comments

0

items should be Xmlroot element and it contain XmlElement item,you have to tell when it Deserialize to object. try this :

public class Item
{
    [XmlElement(ElementName = "sku")]
    public string Sku { get; set; }
    [XmlElement(ElementName = "style")]
    public string Style { get; set; }
    [XmlElement(ElementName = "reason")]
    public string Reason { get; set; }
}

[XmlRoot(ElementName = "items")]
public class Items
{
    [XmlElement(ElementName = "item")]
    public List<Item> Item { get; set; }
}

[XmlRoot(ElementName = "result")]
public class Result
{
    [XmlElement(ElementName = "reporttype")]
    public string Reporttype { get; set; }
    [XmlElement(ElementName = "items")]
    public Items Items { get; set; }
}

Comments

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.