使用C#中的增量元素名称反序列化XML响应

时间:2017-01-13 17:43:03

标签: c# .net xml deserialization

我正在尝试使用ASP.NET C#来使用第三方API,它返回结果,如下面的示例所示,并且由于子对象具有递增的名称,我被绊倒了:

<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>

我的C#类如下所述,通过一些研究,我假设我必须以某种方式实现IXmlSerializable来处理这个问题:

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

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

这是XML的常见模式吗?是否有人对如何序列化这一点有任何想法?我没有使用XML(主要是JSON),因此提前感谢。

2 个答案:

答案 0 :(得分:1)

使用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; }
    }

}

答案 1 :(得分:0)

jdweng的答案是有效的,对于那些有选择使用它的人应该接受它;但是,我的问题是数据是一个更大的XML响应,并使用xml序列化器而不是XElement非常适用于95%的字段(另外5%是我在问题中指定的部分),所以我不想抛弃那些代码 - 相反,我想实现IXmlSerializable接口。

在撕掉我的大部分头发and a little help from this article之后,我终于想出了如何为我的情况实施IXmlSerializable,我想为任何有类似问题的人发布答案,以及社区反馈,因为似乎没有任何关于堆栈溢出的事情:

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();
    }
}

首先,我调用reader.ReadToElement()并将元素名称放在那里。当阅读器传递给ReadXml方法时,它位于请求结果的开头,因此您需要将它放到要序列化的对象的开头。

然后,我读了名为Count的第一个元素。我将值放在Count属性中,然后循环遍历所有结果。请务必注意,您必须阅读结果对象的每个属性,否则您将获得异常。

最后,我阅读了结束标签并继续我的生活。请告诉我您对此实施的看法以及是否可以进行任何改进。

我无法弄清楚的一件事是我有一个结果对象,并且读者有一个我想要使用的方法:reader.ReadElementContentAs(typeof(Result), null) - 看起来这样做会更好,而不是读取每个单独的节点正如我在实施中所做的那样。有谁知道这是怎么做到的吗?