XmlSerializer反序列化具有不同元素名称的列表

时间:2017-01-23 22:21:41

标签: c# xml

我正在尝试使用XmlSerializer解析具有如下所示元素的XML。在amount元素下有很多货币类型,我想将它们反序列化为一个对象集合,这些对象具有保存货币类型的字符串属性和保存金额的整数属性。

有没有任何干净的方法来做到这一点,而无需自定义解析金额。我想将XmlSerializer属性应用于我的类并获得有效的东西。

我无法控制输出XML。

two_factor_recovery_codes

4 个答案:

答案 0 :(得分:1)

我认为最好的办法是进行部分XML解析,并将<amount>元素的内容保留为XmlElement的集合。您仍然需要手动解析它,但您只需手动解析该部分。例如:

[XmlRoot("root")]
public class RecordInfo
{
    [XmlElement("property1")]
    public List<string> Property1;

    [XmlElement("amount")]
    public AmountRawData AmountData;
}

public class AmountRawData
{
    [XmlAnyElement]
    public List<XmlElement> Content;

    public IEnumerable<AmountInfo> Parse()
    {
        foreach (var element in this.Content)
        {
            yield return
                new AmountInfo()
                {
                    Currency = element.LocalName,
                    Type = element.GetAttribute("type"),
                    Amount = element.InnerText,
                };
        }
    }
}

public class AmountInfo
{
    public string Currency;
    public string Type;
    public string Amount;
}

使用示例:

var serializer = new XmlSerializer(typeof(RecordInfo));

var result = (RecordInfo)serializer.Deserialize(dataStream);

foreach (var amount in result.AmountData.Parse())
    Console.WriteLine($"{amount.Currency} {amount.Type} {amount.Amount}");

答案 1 :(得分:0)

正如我在评论中提到的,攻击XML反序列化的最佳方法是从序列化开始。为此,这里有一些属性为control XML serialization的类:

public sealed class root
{
    [XmlElement("property1")]
    public List<string> property1;

    [XmlArrayItem(Type = typeof(EUR))]
    [XmlArrayItem(Type = typeof(USD))]
    public List<amount> amount;
}

public abstract class amount
{
    [XmlAttribute]
    public string type { get; set; }

    [XmlText]
    public string Value { get; set; }
}

public sealed class EUR : amount { }
public sealed class USD : amount { }

测试代码是:

        var root = new root { property1 = new List<string>(), amount = new List<amount>() };
        root.property1.AddRange(new[]{ "a", "b", "c"});
        var eur = new EUR { type = "integer", Value = "1000" };
        var usd = new USD { type = "integer", Value = "1100" };
        root.amount.AddRange(new amount[]{ eur, usd});

生成以下XML:

<?xml version="1.0" encoding="utf-16"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <property1>a</property1>
  <property1>b</property1>
  <property1>c</property1>
  <amount>
    <EUR type="integer">1000</EUR>
    <USD type="integer">1100</USD>
  </amount>
</root>

答案 2 :(得分:0)

这是使用 Cinchoo ETL 的另一种方法 - 一个开源库,用于反序列化如下所示的 XML

  1. 定义 POCO 对象

    [ChoXmlRecordObject]
    public class Root
    {
        [ChoXmlNodeRecordField(XPath = "//property1")]
        public string[] Properties { get; set; }
        [ChoXmlNodeRecordField(XPath = "//amount/*")]
        public double[] Amounts { get; set; }
    }
    
  2. 使用 xml reader 对象反序列化它

        var rec = ChoXmlReader.Deserialize<Root>("*** Xml File Path ***").FirstOrDefault();
        Console.WriteLine(rec.Dump());
    

免责声明:我是这个库的作者。

答案 3 :(得分:-1)

上周使用xml linq回答了类似的问题

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

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

            var results = doc.Elements().Select(x => new {
                property1 = x.Elements("property1").Select(y => (string)y).ToList(),
                dictCurrency = x.Elements("amount").Elements().GroupBy(y => y.Name.LocalName, z => (int)z)
                   .ToDictionary(y => y.Key, z => z.FirstOrDefault())
            }).FirstOrDefault();

        }
    }
}