如何解决XmlSerializer:反序列化递归对象图
鉴于我想反序列化以下 XML:
<?xml version="1.0" encoding="utf-8" ?>
<units>
<entity>
<health max="1000"/>
<sprite texture="tank"/>
<entity>
<sprite texture="tank-turret"/> <!-- this element is missing when i deserialize --!>
</entity>
</entity>
</units>
如何使用 XmlSerializer
反序列化这个递归对象图?
以下是我的最后一次尝试。它成功地反序列化了顶级对象(健康、精灵、实体),但它似乎没有在嵌套的实体节点中找到精灵元素。 我也尝试从 componentlist 派生实体,但它也不起作用。
public class UnitSerializer
{
public abstract class item
{
}
public class entity : item
{
[XmlArray("entity")]
[XmlArrayItem(typeof(health))]
[XmlArrayItem(typeof(entity))]
[XmlArrayItem(typeof(sprite))]
public componentlist entity2 { get; set; }
}
public abstract class component : item
{
}
public class health : component
{
[XmlAttribute]
public int max { get; set; }
}
public class sprite : component
{
[XmlAttribute]
public string texture { get; set; }
}
public class componentlist : List<item>
{
}
[XmlRoot("units")]
public class units
{
[XmlArray("entity")]
[XmlArrayItem(typeof(health))]
[XmlArrayItem(typeof(entity))]
[XmlArrayItem(typeof(sprite))]
public componentlist entity { get; set; }
}
public void Read()
{
var x = new XmlSerializer(typeof(units),new[] {
typeof(componentlist),typeof(entity),typeof(health),typeof(sprite)
});
var fs = new FileStream("units.xml",FileMode.Open);
XmlReader reader = new XmlTextReader(fs);
var units = (units)x.Deserialize(reader);
}
}
解决方法
您的类可以通过将 [XmlArray]
和 [XmlArrayItem]
的使用替换为 [XmlElement(typeof(TDerived))]
来修复:
public class UnitSerializer
{
public abstract class item
{
}
public class entity : item
{
[XmlElement("health",typeof(health))]
[XmlElement("entity",typeof(entity))]
[XmlElement("sprite",typeof(sprite))]
public List<item> EntityList { get; set; }
}
public abstract class component : item
{
}
public class health : component
{
[XmlAttribute]
public int max { get; set; }
}
public class sprite : component
{
[XmlAttribute]
public string texture { get; set; }
}
[XmlRoot("units")]
public class units
{
[XmlElement("health",typeof(sprite))]
public List<item> EntityList { get; set; }
}
public units Read(string filename)
{
var x = new XmlSerializer(typeof(units));
using (var fs = new FileStream(filename,FileMode.Open))
using (var reader = XmlReader.Create(fs))
{
return (units)x.Deserialize(reader);
}
}
}
注意事项:
-
[XmlArray]
表示应该使用包含元素序列的外部包装元素序列化集合,而[XmlElement]
表示应该将集合序列化为没有包装的序列。您的 XML 示例使用没有包装元素的重复元素,因此应使用[XmlElement]
。它有点工作,因为您的 XML 是递归的——但在其他所有级别,重复元素都被错误地反序列化为包装器。这就解释了为什么在反序列化过程中会丢失部分而非全部数据。 -
在您的 XML 示例中,多态元素由元素名称标识。
XmlSerializer(Type,Type[])
构造函数应用于指定要使用xsi:type
机制序列化的多态包含类型。由于xsi:type
属性未出现在您的 XML 中,因此无需使用此构造函数。(此外,在使用
XmlSerializer
构造函数构造XmlSerializer(Type,Type[])
时,您必须静态缓存序列化程序以避免严重的内存泄漏。参见 Memory Leak using StreamReader and XmlSerializer为什么。) -
XmlTextReader
自 .Net 2.0 起已弃用。改用XmlReader.Create()
。 -
应该处理
FileStream
和XmlReader
,最好通过using
语句进行处理。 -
我去掉了
public class componentlist : List<item>
,只用一个List<item>
代替了它。这主要是一个品味问题,但它确实可以更轻松地使用 Linq 的.ToList()
设置此类列表的值。
演示小提琴here。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。