XmlSerializer:反序列化递归对象图
XmlSerializer: deserialize recursive object graph
给定以下 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
反序列化此递归对象图?
以下是我最后一次尝试。它成功地反序列化了顶级对象(health、sprite、entity),但它似乎没有在嵌套的实体节点中找到 sprite 元素。
我也试过从组件列表派生实体,但它也没有用。
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]
and [XmlArrayItem]
with [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(health))]
[XmlElement("entity", typeof(entity))]
[XmlElement("sprite", 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[])
constructor should be used to specify polymorphic included types to be serialized using the xsi:type
mechanism. Since the xsi:type
属性没有出现在你的XML中,不需要使用这个构造函数。
(此外,当使用XmlSerializer(Type, Type[])
构造函数构造XmlSerializer
时,必须静态缓存序列化程序以避免严重的内存泄漏。参见Memory Leak using StreamReader and XmlSerializer 为什么。)
XmlTextReader
自 .Net 2.0 以来已被弃用。请改用 XmlReader.Create()
。
应该处理 FileStream
和 XmlReader
,最好通过 using
语句。
我删除了 public class componentlist : List<item>
并仅用 List<item>
替换了它。这主要是个人喜好问题,但使用 Linq 的 .ToList()
.
确实可以更轻松地设置此类列表的值
演示 fiddle here.
给定以下 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
反序列化此递归对象图?
以下是我最后一次尝试。它成功地反序列化了顶级对象(health、sprite、entity),但它似乎没有在嵌套的实体节点中找到 sprite 元素。 我也试过从组件列表派生实体,但它也没有用。
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]
and [XmlArrayItem]
with [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(health))]
[XmlElement("entity", typeof(entity))]
[XmlElement("sprite", 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[])
constructor should be used to specify polymorphic included types to be serialized using thexsi:type
mechanism. Since thexsi:type
属性没有出现在你的XML中,不需要使用这个构造函数。(此外,当使用
XmlSerializer(Type, Type[])
构造函数构造XmlSerializer
时,必须静态缓存序列化程序以避免严重的内存泄漏。参见Memory Leak using StreamReader and XmlSerializer 为什么。)XmlTextReader
自 .Net 2.0 以来已被弃用。请改用XmlReader.Create()
。应该处理
FileStream
和XmlReader
,最好通过using
语句。我删除了
确实可以更轻松地设置此类列表的值public class componentlist : List<item>
并仅用List<item>
替换了它。这主要是个人喜好问题,但使用 Linq 的.ToList()
.
演示 fiddle here.