如何将不同类型的子元素反序列化为基本类型的 list/collection,即相应 class 的 属性
How to deserialize child elements of different types into list/collection of base type which is a property of the corresponding class
我有一个遗留系统,它使用一些以 XML 格式存储的数据。 XML 的格式不能在不引起严重问题(即读取格式的工具和其他软件)的情况下更改。
XML 的结构如下:
<Things>
<ThingOne Name="FirstThing">
<ThingOne Name="FirstChildThing" />
<ThingTwo Name="SecondChildThing"/>
</ThingOne>
<ThingTwo Name="SecondThing">
<ThingOne Name="ThirdChildThing" />
<ThingTwo Name="FourthChildThing" />
</ThingTwo>
</Things>
在我的场景中,“ThingOne”和“ThingTwo”是 class扩展“SomeThing”的元素。
像这样:
public abstract class SomeThing
{
public int ItemNumber {get; set;}
public string Name {get; set;}
public List<SomeThing> ChildThings {get; set;}
public abstract void InspectChildren();
}
public sealed class ThingOne : SomeThing
{
public override void InspectChildren()
{
/*Iterates Child Things and performs actions*/
}
}
public sealed class ThingTwo : SomeThing
{
public override void InspectChildren()
{
/*Iterates Child Things and performs actions different from thing one*/
}
}
而“Things”是一个 class,包含“SomeThing”的集合:
public sealed class Things
{
/*Thing One and Thing Two should be deserialized to here*/
public List<SomeThing> ThingItems {get; set;}
}
有个检查员是这样的:
public static ThingInspector
{
public static void InspectThings(IEnumerable<SomeThing> thingsToInspect)
{
foreach(var thing in thingsToInspect)
{
thing.InspectChildren()
}
}
}
然而,当我使用 XmlSerializer 将 XML 反序列化为“Things”的实例时,它不会拾取“ThingOne”和“ThingTwo”并将它们添加到 ThingItems。
我试过如下装饰 classes:
public sealed class Things:IList<SomeThing>
{
/*Thing One and Thing Two should be deserialized to here*/
[XmlArrayItem(typeof(ThingOne), ElementName = "ThingOne")]
[XmlArrayItem(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ThingItems {get; set;}
/*Example doesn't include IList implementation, but it is present and correct*/
}
public abstract class SomeThing
{
[XmlIgnore]
public int ItemNumber {get; set;}
[XmlAttribute]
public string Name {get; set;}
[XmlArrayItem(typeof(ThingOne), ElementName = "ThingOne")]
[XmlArrayItem(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ChildThings {get; set;}
public abstract void InspectChildren();
}
反序列化的对象是“Things”的实例,但 ThingItems 是空的。
我错过了什么?我已经尝试更改 xml 格式,以便将“ThingOne”和“ThingTwo”包装在“ThingItems”列表中并且工作正常,但生产系统中的 xml 格式无法更改,否则会导致其他正在读取和写入 xml 数据的软件出现故障。
我的问题是我需要将“Things”的子元素反序列化到相应“Things”的列表中class,而不是将对象结构强制到XML。任何指导将不胜感激。
您可以通过如下修改类型来反序列化您的 XML:
public interface IHasThings
{
List<SomeThing> ChildThings { get; set; }
}
[XmlRoot("Things")]
public class Things : IHasThings
{
[XmlElement(typeof(ThingOne), ElementName = "ThingOne")]
[XmlElement(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ChildThings { get; set; } = new List<SomeThing>();
}
public abstract class SomeThing : IHasThings
{
[XmlIgnore]
public int ItemNumber { get; set; }
[XmlAttribute]
public string Name {get; set;}
[XmlElement(typeof(ThingOne), ElementName = "ThingOne")]
[XmlElement(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ChildThings {get; set;} = new List<SomeThing>();
public abstract void InspectChildren();
}
备注:
通过将 [XmlElementAttribute]
应用于您的列表,列表元素将被序列化为父元素的直接子元素,而无需插入额外的中间容器元素。
多态性通过应用 [XmlElement(typeof(TThing), ElementName = "ThingName")]
which specifies an alternate XML element, name for that specific polymorphic child type, similarly to how XmlArrayItemAttribute(String, Type)
处理,适用于具有容器元素的集合。 XmlArrayItem
和 XmlElement
不应应用于相同的 属性。
引入 IHasThings
是可选的,但可以更轻松地遍历层次结构,以统一的方式处理根容器和子对象。
ThingOne
和ThingTwo
不变。
调试 XML 反序列化问题的一个好方法是手动构建预期的反序列化图,然后将其序列化。观察到的和预期的 XML 之间的任何差异都可能表明您的反序列化错误所在。使用您的数据模型,您会看到子集合的额外级别的容器元素 <ChildThings>
。
演示 fiddle here.
我有一个遗留系统,它使用一些以 XML 格式存储的数据。 XML 的格式不能在不引起严重问题(即读取格式的工具和其他软件)的情况下更改。
XML 的结构如下:
<Things>
<ThingOne Name="FirstThing">
<ThingOne Name="FirstChildThing" />
<ThingTwo Name="SecondChildThing"/>
</ThingOne>
<ThingTwo Name="SecondThing">
<ThingOne Name="ThirdChildThing" />
<ThingTwo Name="FourthChildThing" />
</ThingTwo>
</Things>
在我的场景中,“ThingOne”和“ThingTwo”是 class扩展“SomeThing”的元素。 像这样:
public abstract class SomeThing
{
public int ItemNumber {get; set;}
public string Name {get; set;}
public List<SomeThing> ChildThings {get; set;}
public abstract void InspectChildren();
}
public sealed class ThingOne : SomeThing
{
public override void InspectChildren()
{
/*Iterates Child Things and performs actions*/
}
}
public sealed class ThingTwo : SomeThing
{
public override void InspectChildren()
{
/*Iterates Child Things and performs actions different from thing one*/
}
}
而“Things”是一个 class,包含“SomeThing”的集合:
public sealed class Things
{
/*Thing One and Thing Two should be deserialized to here*/
public List<SomeThing> ThingItems {get; set;}
}
有个检查员是这样的:
public static ThingInspector
{
public static void InspectThings(IEnumerable<SomeThing> thingsToInspect)
{
foreach(var thing in thingsToInspect)
{
thing.InspectChildren()
}
}
}
然而,当我使用 XmlSerializer 将 XML 反序列化为“Things”的实例时,它不会拾取“ThingOne”和“ThingTwo”并将它们添加到 ThingItems。
我试过如下装饰 classes:
public sealed class Things:IList<SomeThing>
{
/*Thing One and Thing Two should be deserialized to here*/
[XmlArrayItem(typeof(ThingOne), ElementName = "ThingOne")]
[XmlArrayItem(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ThingItems {get; set;}
/*Example doesn't include IList implementation, but it is present and correct*/
}
public abstract class SomeThing
{
[XmlIgnore]
public int ItemNumber {get; set;}
[XmlAttribute]
public string Name {get; set;}
[XmlArrayItem(typeof(ThingOne), ElementName = "ThingOne")]
[XmlArrayItem(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ChildThings {get; set;}
public abstract void InspectChildren();
}
反序列化的对象是“Things”的实例,但 ThingItems 是空的。
我错过了什么?我已经尝试更改 xml 格式,以便将“ThingOne”和“ThingTwo”包装在“ThingItems”列表中并且工作正常,但生产系统中的 xml 格式无法更改,否则会导致其他正在读取和写入 xml 数据的软件出现故障。
我的问题是我需要将“Things”的子元素反序列化到相应“Things”的列表中class,而不是将对象结构强制到XML。任何指导将不胜感激。
您可以通过如下修改类型来反序列化您的 XML:
public interface IHasThings
{
List<SomeThing> ChildThings { get; set; }
}
[XmlRoot("Things")]
public class Things : IHasThings
{
[XmlElement(typeof(ThingOne), ElementName = "ThingOne")]
[XmlElement(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ChildThings { get; set; } = new List<SomeThing>();
}
public abstract class SomeThing : IHasThings
{
[XmlIgnore]
public int ItemNumber { get; set; }
[XmlAttribute]
public string Name {get; set;}
[XmlElement(typeof(ThingOne), ElementName = "ThingOne")]
[XmlElement(typeof(ThingTwo), ElementName = "ThingTwo")]
public List<SomeThing> ChildThings {get; set;} = new List<SomeThing>();
public abstract void InspectChildren();
}
备注:
通过将
[XmlElementAttribute]
应用于您的列表,列表元素将被序列化为父元素的直接子元素,而无需插入额外的中间容器元素。多态性通过应用
[XmlElement(typeof(TThing), ElementName = "ThingName")]
which specifies an alternate XML element, name for that specific polymorphic child type, similarly to howXmlArrayItemAttribute(String, Type)
处理,适用于具有容器元素的集合。XmlArrayItem
和XmlElement
不应应用于相同的 属性。引入
IHasThings
是可选的,但可以更轻松地遍历层次结构,以统一的方式处理根容器和子对象。ThingOne
和ThingTwo
不变。调试 XML 反序列化问题的一个好方法是手动构建预期的反序列化图,然后将其序列化。观察到的和预期的 XML 之间的任何差异都可能表明您的反序列化错误所在。使用您的数据模型,您会看到子集合的额外级别的容器元素
<ChildThings>
。
演示 fiddle here.