关于 c#:XML 反序列化非基于集合的响应

XML Deserialization For Non-Collection Based Responses

我正在使用 API 来获取有关 Web 应用程序的一些信息。我编写了类来反序列化 XML 响应并添加了 XMLRoot、XMLElement 属性。对于带有子项集合的 XML 响应,我可以使用属性进行反序列化。例如

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<time-entries>
   <time-entry>
        //Other sub nodes
   </time-entry>
   <time-entry>
        //Other sub nodes
   </time-entry>
</time-entries>

对于像上面这样的 XML 响应,我编写了一个 TimeEntry 类,该类具有时间入口节点的其他属性的属性。然后我用 TimeEntry 类的集合编写了另一个类,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
[XmlRoot("time-entries")]
public class TimeEntryResponse
{
    public TimeEntryResponse()
    {

    }

    [XmlElement("time-entry")]
    public List<TimeEntry> TimeEntries { get; set; }

}

因此,使用 TimeEntryResponse 类,我可以反序列化 XML 响应,例如问题的顶部。

但我不能像下面的响应那样反序列化。

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<time-totals>
   <total-mins-sum type="integer">382743</total-mins-sum>
   <non-billed-mins-sum type="integer">328988</non-billed-mins-sum>
   <non-billable-hours-sum type="integer">3137.30</non-billable-hours-sum>
</time-totals>

我还为这个响应写了一个 TimeTotal 类。

1
2
3
4
5
6
7
8
9
10
[XmlRoot("time-totals")]
public class TimeTotal
{
    [XmlElement("total-mins-sum")]
    public double TotalMinsSum { get; set; }
    [XmlElement("non-billed-mins-sum")]
    public double NonBilledMinsSum { get; set; }
    [XmlElement("non-billable-hours-sum")]
    public double NonBillableHoursSum { get; set; }
}

然后我写了一个响应类。

1
2
3
4
5
6
7
8
9
public class TimeTotalsResponse : IEntityResponse
{
    public TimeTotalsResponse()
    {

    }

    public TimeTotal TimeTotal { get; set; }
}

如您所见,没有此响应的集合,而且我不知道应该添加响应类 TimeTotal 的哪些属性。

也许我可以直接将 TimeTotal 类的属性放入 TimeTotalResponse 类。但我将使用这个类来反序列化包含时间总计节点的类。


我不认为您可以使用开箱即用的 XmlSerialization 来做到这一点,因为您的对象不会直接映射到 XML。我能想到两种方法。

方法一:

由于 XML 可以直接反序列化为 TimeTotal,因此首先使用简单的静态方法序列化为 TimeTotal,并将其设置为新创建的 TimeTotalsResponse 的属性,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
static void Main(string[] args)
{
     var response = GetResponse(File.ReadAllText("XMLFile1.xml"));
}

static TimeTotalsResponse GetResponse(string xml)
{
    using (StringReader reader = new StringReader(xml))
    {
        var ser = new XmlSerializer(typeof(TimeTotal));
        return new TimeTotalsResponse() { TimeTotal = (TimeTotal)ser.Deserialize(reader) };
    }
}

方法二:
通过编写自己的 XmlSerializer 来自定义序列化,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TimeTotalsResponseSerializer : IXmlSerializable
{
    public TimeTotalsResponse Response { get; set; }

    public void ReadXml(XmlReader reader)
    {
        Response = new TimeTotalsResponse();
        Response.TimeTotal = new TimeTotal();

        reader.ReadToDescendant("total-mins-sum");
        Response.TimeTotal.TotalMinsSum = reader.ReadElementContentAsDouble();
        Response.TimeTotal.NonBilledMinsSum = reader.ReadElementContentAsDouble();
        Response.TimeTotal.NonBillableHoursSum = reader.ReadElementContentAsDouble();
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void WriteXml(XmlWriter writer)
    {
    }
}

用法是;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void Main(string[] args)
{
    XmlReaderSettings settings = new XmlReaderSettings()
    {
        IgnoreWhitespace = true
    };

    string xml = File.ReadAllText("XMLFile1.xml");
    using (XmlReader reader = XmlReader.Create(new StringReader(xml), settings))
    {
        var ser = new TimeTotalsResponseSerializer();
        ser.ReadXml(reader);
        var response = ser.Response;
    }
}

查看问题的最佳方法是对数据进行序列化。请参阅下面的代码。查看为 xsi:type="TimeEntries" 和 xsi:type="TimeTotalsResponse"

生成的两个 XML 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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 FILENAME1 = @"c:\\temp\\test1.xml";
        const string FILENAME2 = @"c:\\temp\\test2.xml";
        static void Main(string[] args)
        {
            TimeEntries timeEntries = new TimeEntries()
            {
                timeEntry = new List<TimeEntry>() {
                    new TimeEntry() {},
                    new TimeEntry() {},
                    new TimeEntry() {}
                }
            };
            TimeTotalsResponse timeTotals = new TimeTotalsResponse()
            {
                TotalMinsSum = new TypeInteger() { _type ="integer", _value = 382743 },
                NonBilledMinsSum = new TypeInteger() { _type ="integer", _value = 328988 },
                NonBillableHoursSum = new TypeInteger() { _type ="integer", _value = 3137.30 }
            };

            XmlSerializer serializer = new XmlSerializer(typeof(TimeEntryResponse));

            StreamWriter writer = new StreamWriter(FILENAME1);
            serializer.Serialize(writer, timeEntries);
            writer.Flush();
            writer.Close();
            writer.Dispose();

            writer = new StreamWriter(FILENAME2);
            serializer.Serialize(writer, timeTotals);
            writer.Flush();
            writer.Close();
            writer.Dispose();


        }
    }
    [XmlInclude(typeof(TimeEntries))]
    [XmlInclude(typeof(TimeTotalsResponse))]
    [Serializable]
    public class TimeEntryResponse
    {

    }
    [XmlRoot("time-entries")]
    public class TimeEntries : TimeEntryResponse
    {
        [XmlElement("time-entry")]
        public List<TimeEntry> timeEntry { get; set; }
    }
    [XmlRoot("time-entry")]
    public class TimeEntry
    {
    }
    [XmlRoot("time-totals")]
    public class TimeTotalsResponse : TimeEntryResponse
    {

        [XmlElement("total-mins-sum")]
        public TypeInteger  TotalMinsSum { get; set; }
        [XmlElement("non-billed-mins-sum")]
        public TypeInteger NonBilledMinsSum { get; set; }
        [XmlElement("non-billable-hours-sum")]
        public TypeInteger NonBillableHoursSum { get; set; }
    }
    public class TypeInteger
    {
        [XmlAttribute("type")]
        public string _type { get; set; }
        [XmlText]
        public double _value { get; set; }
    }
}
a€?