使用动态 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,Measurement
,Time
,ID
总是会在object。
LeftWheel
和 RightWheel
元素总是会和 ValuesRead
一起存在,但是 ValuesRead
children 是动态的,可以是任何东西。
我尝试制作一个 C# object 来反映大部分结构,然后使用 XmlSerializer.UnknownElement
来获取 ValuesRead
元素中的未知元素,但我不能 link 到上面的 parent 就知道是在 LeftWheel
还是 RightWheel
.
XmlSerializer serializer = new XmlSerializer(typeof(FVISSiteEvent));
serializer.UnknownElement += UnknownElementFound;
有没有一种方法可以将 LeftWheel
和 RightWheel
类 定义为动态的序列化,同时让另一个 类 不是动态的?
您应该能够使用 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
,然后我从那里迭代节点,使用 Name
和 InnerText
值来读取所有数据。
我正在尝试将一些 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,Measurement
,Time
,ID
总是会在object。
LeftWheel
和 RightWheel
元素总是会和 ValuesRead
一起存在,但是 ValuesRead
children 是动态的,可以是任何东西。
我尝试制作一个 C# object 来反映大部分结构,然后使用 XmlSerializer.UnknownElement
来获取 ValuesRead
元素中的未知元素,但我不能 link 到上面的 parent 就知道是在 LeftWheel
还是 RightWheel
.
XmlSerializer serializer = new XmlSerializer(typeof(FVISSiteEvent));
serializer.UnknownElement += UnknownElementFound;
有没有一种方法可以将 LeftWheel
和 RightWheel
类 定义为动态的序列化,同时让另一个 类 不是动态的?
您应该能够使用 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
,然后我从那里迭代节点,使用 Name
和 InnerText
值来读取所有数据。