简体   繁体   中英

Deserialize XML Response with Incremented Element Names in C#

I am trying to consume a 3rd party API using ASP.NET C# that returns results as seen in the example below and I am getting tripped up on the fact that the child objects have incremented names:

<Results>
    <Count>3</Count>
    <Result1>
        <Id>1</Id>
        <Property1>value</Property1>
        <Property2>value</Property2>
        ...
        <PropertyN>value</PropertyN>
    </Result1>
    <Result2>...properties...</Result2>
    <Result3>...properties...</Result3>
</Results>

My C# class is as outlined below, and through some research I am assuming that I have to implement IXmlSerializable to handle this somehow:

public class Results : IXmlSerializable
{
    [XmlElement("Count")]
    public int Count { get; set; }

    public List<Result> ResultItems { get; set; }
}

Is this a common pattern for XML and does anyone have any ideas on how to serialize this? I don't work a lot with XML (mostly JSON), so thanks in advance.

Using xml linq

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

namespace ConsoleApplication42
{
    class Program
    {
        static void Main(string[] args)
        {
            string xml =
                "<Root>" +
                "<Results>" +
                    "<Count>3</Count>" +
                    "<Result1>...properties...</Result1>" +
                    "<Result2>...properties...</Result2>" +
                    "<Result3>...properties...</Result3>" +
                "</Results>" +
                "</Root>";

            XElement xResults = XElement.Parse(xml);

            Results results = xResults.Elements("Results").Select(x => new Results() {
                Count = (int)x.Element("Count"),
                ResultItems = x.Elements().Where(y => y.Name.LocalName.StartsWith("Result")).Select(y => (string)y).ToList()
            }).FirstOrDefault();

        }


    }
    public class Results
    {

        public int Count { get; set; }

        public List<string> ResultItems { get; set; }
    }

}

jdweng's answer works, and for those of you who have an option to use that should take it; however, my issue is the data is in a much larger XML response and using the xml serializer instead of XElement works really well with 95% of the fields (the other 5% being the section I specified in the question), so I don't want to jettison that code - instead, I want to implement the IXmlSerializable interface.

After tearing out most of my hair and a little help from this article , I finally figured out how to implement IXmlSerializable for my situation, and I wanted to post the answer for anyone who has a similar issue, as well as community feedback since there didn't seem to be anything on stack overflow about this:

public class Results : IXmlSerializable
{
    public int Count { get; set; }

    public List<Result> ResultItems { get; set; }

    public Results()
    {
        ResultItems = new List<Result>();
    }

    public XmlSchema GetSchema()
    {
        return (null);
    }

    public void ReadXml(XmlReader reader)
    {
        reader.ReadStartElement("Results");

        if(reader.Name == "Count")
        {
            Count = reader.ReadElementContentAsInt();
        }

        for (int i = Count; i > 0; i--)
        {
            var result = new Result();
            reader.ReadStartElement("Result" + i);

            result.Property1 = reader.ReadElementContentAsInt();
            result.Property2 = reader.ReadElementContentAsString();
            ...
            ...
            result.PropertyN = reader.ReadElementContentAsString();

            ResultItems.Add(result);
        }

        reader.ReadEndElement();
    }

    public void WriteXml(XmlWriter writer)
    {
        //I don't ever need to write this to XML, 
        //so I'm not going to implement this
        throw new NotImplementedException();
    }
}

First, I call reader.ReadToElement() and put the element name in there. When the reader gets passed to the ReadXml method, it is at the beginning of the request result, so you need to get it to the beginning of the object you want to serialize.

Then, I read the first element whose name is Count. I put the value in the Count property and then loop through all of the results. It is important to note that you must read every property of the result object, otherwise you will get exceptions.

Lastly, I read the closing tag and move on with my life. Please let me know what you think about this implementation and if there are any improvements that could be made.

One thing I could not figure out is that I have a results object and the reader has a method that I want to use: reader.ReadElementContentAs(typeof(Result), null) - it seems it would be nicer to do that instead of reading each individual node as I have done in my implementation. Does anyone know how to do that?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM