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()

  • 应该处理 FileStreamXmlReader,最好通过 using 语句。

  • 我删除了 public class componentlist : List<item> 并仅用 List<item> 替换了它。这主要是个人喜好问题,但使用 Linq 的 .ToList().

    确实可以更轻松地设置此类列表的值

演示 fiddle here.