使用动态 child 元素将 XML 反序列化为 object

Deserialize XML into object with dynamic child elements

我正在尝试将一些 xml 反序列化为 C# object。在大多数情况下,诀窍是我知道这个 object 会是什么样子。但是,这是一个具有动态元素的child。

(这里有一个例子)

<measurement>
  <Time>2021-02-02</Time>
  <ID>1</ID>
  <LeftWheel>
  <ValuesRead>
    <DynamicValue>12.3</DynamicValue>
    <DynamicValue2>2.3</DynamicValue2>
    <DynamicValue4>1.3</DynamicValue4>
    <DynamicValue3>10.3</DynamicValue3>
  </ValuesRead>
  </LeftWheel>
      <RightWheel>
  <ValuesRead>
    <DynamicValue>12.3</DynamicValue>
    <DynamicValue2>2.3</DynamicValue2>
    <DynamicValue6>1.3</DynamicValue6>
    <DynamicValue10>10.3</DynamicValue10>
  </ValuesRead>
  </RightWheel>
</measurement>

在这个XML,MeasurementTimeID总是会在object。 LeftWheelRightWheel 元素总是会和 ValuesRead 一起存在,但是 ValuesRead children 是动态的,可以是任何东西。

我尝试制作一个 C# object 来反映大部分结构,然后使用 XmlSerializer.UnknownElement 来获取 ValuesRead 元素中的未知元素,但我不能 link 到上面的 parent 就知道是在 LeftWheel 还是 RightWheel.

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

serializer.UnknownElement += UnknownElementFound;

有没有一种方法可以将 LeftWheelRightWheel 类 定义为动态的序列化,同时让另一个 类 不是动态的?

您应该能够使用 UnknownElementFound 事件手动处理序列化的这些方面。参见:

其他选项可能是将您希望看到的类型指定为 XmlElementAtrribute 装饰属性,如果不反序列化,它们将只是 null。

还有在 class 中实现 IXmlSerializable 并完全控制反序列化的核心选项。

使用自定义序列化程序:

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

namespace ConsoleApplication16
{
    class Program
    {
        const string INPUT_FILENAME = @"c:\temp\test.xml";
        const string OUTPUT_FILENAME = @"c:\temp\test1.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(INPUT_FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(Measurement));
            Measurement measurement = (Measurement)serializer.Deserialize(reader);

            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME,settings);
            serializer.Serialize(writer, measurement);
 
        }
    }
    [XmlRoot("measurement")]
    public class Measurement
    {
        public DateTime Time { get; set; }
        public int ID { get; set; }

        [XmlArray("LeftWheel")]
        [XmlArrayItem("ValuesRead")]
        public List<Wheel> leftWheel { get; set; }

        [XmlArray("RightWheel")]
        [XmlArrayItem("ValuesRead")]
        public List<Wheel> rightWheel { get; set; }
    }
    public class Wheel : IXmlSerializable
    {
        List<decimal> values { get; set; }

        // Xml Serialization Infrastructure

        public void WriteXml(XmlWriter writer)
        {
            int count = 0;
            XElement valuesRead = new XElement("ValuesRead");
            for (int i = 0; i < values.Count; i++ )
            {
                valuesRead.Add(new XElement("ValuesRead" + (i == 0? "" : i.ToString()), values[i]));
            }
            writer.WriteRaw(valuesRead.ToString());

        }

        public void ReadXml(XmlReader reader)
        {
            XElement values = (XElement)XElement.ReadFrom(reader);
            this.values = values.Elements().Where(x => x.Name.LocalName.StartsWith("DynamicValue")).Select(x => (decimal)x).ToList(); 
        }

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


        
    } 
}

除了使用自定义 Xml 序列化来反序列化您的 xml 文件之外,这是使用 Cinchoo ETL 的另一种方法 - 一个开源库来处理它的简单方法(那些打开尝试它!)

定义 POCO Class

public class Measurement
{
    public DateTime Time { get; set; }
    public int ID { get; set; }
    [ChoXPath("LeftWheel/ValuesRead/*")]
    public double[] LeftWheel { get; set; }
    [ChoXPath("RightWheel/ValuesRead")]
    public dynamic RightWheel { get; set; }
}

使用 ChoETL 反序列化

using (var r = ChoXmlReader<Measurement>.LoadText(xml)
       .WithXPath("/")
      )
{
    foreach (var rec in r)
         rec.Print();
}

示例 fiddle:https://dotnetfiddle.net/KtNvra

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

我在反序列化时使用动态类型设法解决了这个问题。 当我反序列化 ValuesRead 时,它被定义为动态类型。

当反序列化时,它变成一个 XmlNode,然后我从那里迭代节点,使用 NameInnerText 值来读取所有数据。