【问题标题】:XmlSerializer: deserialize recursive object graphXmlSerializer:反序列化递归对象图
【发布时间】:2021-06-24 11:57:06
【问题描述】:

鉴于我想反序列化以下 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 反序列化这个递归对象图?

以下是我最后一次尝试。它成功地反序列化了顶级对象(健康、精灵、实体),但它似乎没有在嵌套实体节点中找到精灵元素。 我也尝试过从组件列表中派生实体,但也没有用。

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);
    }
}

【问题讨论】:

    标签: c# .net xml-serialization xmlserializer


    【解决方案1】:

    您的课程可以通过将 [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(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[]) 构造函数应该用于指定要使用xsi:type 机制序列化的多态包含类型。由于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&lt;item&gt; 并用 List&lt;item&gt; 替换它。这主要是一个口味问题,但它确实可以更轻松地使用 Linq 的 .ToList() 设置此类列表的值。

    演示小提琴here.

    【讨论】:

    • 感谢您的出色而令人筋疲力尽的回答!我现在也可以通过从 entity 初始化单元来摆脱重复的 EntityList 属性定义。
    猜你喜欢
    • 1970-01-01
    • 2014-06-10
    • 2016-09-05
    • 1970-01-01
    • 2010-11-02
    • 2011-02-07
    • 2011-10-04
    • 1970-01-01
    • 2023-04-05
    相关资源
    最近更新 更多