C# Xmlserializer 将列表反序列化为 0 而不是 null

Posted

技术标签:

【中文标题】C# Xmlserializer 将列表反序列化为 0 而不是 null【英文标题】:C# Xml Serializer deserializes list to count of 0 instead of null 【发布时间】:2021-11-29 13:25:00 【问题描述】:

我对 XmlSerializer 在幕后的工作方式感到困惑。我有一个将 XML 反序列化为对象的类。我看到的是以下两个元素,它们不是被反序列化的 Xml 的一部分。

[XmlRootAttribute("MyClass", Namespace = "", IsNullable = false)]
public class MyClass

    private string comments;
    public string Comments
    
        set  comments = value; 
        get  return comments; 
    

    private System.Collections.Generic.List<string> tests = null;
    public System.Collections.Generic.List<string> Tests
    
        get  return tests; 
        set  tests = value; 
    

我们以下面的 XML 为例:

<MyClass>
  <SomeNode>value</SomeNode>
</MyClass>

您注意到测试和注释不是 XML 的一部分。

当这个 XML 被反序列化时,Comments 为 null(这是预期的)并且 Tests 是一个计数为 0 的空列表。

如果有人可以向我解释这一点,将不胜感激。我更喜欢的是,如果 XML 中缺少 &lt;Tests&gt;,则列表应保持为空,但如果存在(可能为空)节点 &lt;Tests /&gt;,则应分配列表。

【问题讨论】:

@gdir 你是说当你序列化这个时你得到一个列表的空值而不是空列表? 不,我要问的是,由于测试不是 xml 的一部分,因此在序列化时它应该为 null 而不是空列表。这有意义吗? OP 的问题是关于反序列化而不是序列化。当上面的 XML 被反序列化时,即使 &lt;Tests /&gt; 从未出现在 XML 中,也会分配 tests 集合。 (顺便说一句,我可以复制它。) 编辑反序列化谢谢 我不知道有什么地方记录了这种行为。当&lt;Tests&gt; 完全丢失时,您不希望分配列表。当有一个空的&lt;Tests /&gt; 节点时你想要什么? 【参考方案1】:

您观察到的是,引用可修改集合(例如 List&lt;T&gt;)的成员在反序列化开始时由 XmlSerializer 自动预分配。我不知道记录此行为的任何地方。这可能与this answer 到XML Deserialization of collection property with code defaults 中描述的行为有关,这说明,由于XmlSerializersupports adding to get-only and pre-allocated collections,如果预分配的集合包含默认项 em> 然后反序列化的项目将被附加到它 - 可能会复制内容。 Microsoft 可能只是选择在反序列化开始时预分配 所有 可修改的集合,作为实现这一点的最简单方法。

该答案的解决方法,即使用代理数组属性,也适用于此。由于无法追加数组,XmlSerializer 必须累积所有值并在反序列化完成时将它们设置回来。但如果从未遇到相关标签,XmlSerializer 显然不会开始累积值,因此不会调用数组设置器。这似乎可以防止您不想要的集合的默认预分配:

[XmlRootAttribute("MyClass", Namespace = "", IsNullable = false)]
public class MyClass

    private string comments;
    public string Comments
    
        set  comments = value; 
        get  return comments; 
    

    private System.Collections.Generic.List<string> tests = null;

    [XmlIgnore]
    public System.Collections.Generic.List<string> Tests
    
        get  return tests; 
        set  tests = value; 
    

    [XmlArray("Tests")]
    public string[] TestsArray
    
        get
        
            return (Tests == null ? null : Tests.ToArray());
        
        set
        
            if (value == null)
                return;
            (Tests = Tests ?? new List<string>(value.Length)).AddRange(value);
        
    

示例.Net fiddle 表明Tests 仅在适当时分配。

【讨论】:

【参考方案2】:

当你对属性应用[System.Xml.Serialization.XmlElement(IsNullable = true)]时,List在反序列化后会为空。

【讨论】:

【参考方案3】:

另一种可能是使用“魔法”“指定”后缀:

public bool TestsSpecified get;set;

如果您有一个序列化的字段/属性 XXX 和一个布尔属性 XXXSpecified,则根据是否设置了主字段/属性来设置布尔属性。

【讨论】:

【参考方案4】:

在谷歌搜索相同的问题后,我们来到这里。 我们最终做的是在反序列化之后检查 Count == 0,并手动将属性设置为 null;

...
var varMyDeserializedClass = MyXmlSerializer.Deserialize(new StringReader(myInput));
if (varMyDeserializedClass.ListProperty.Count == 0)

  varMyDeserializedClass.ListProperty = null;

...

这是一种廉价的解决方法,但提供了预期的结果,并且有助于避免重构或重新设计。

【讨论】:

以上是关于C# Xmlserializer 将列表反序列化为 0 而不是 null的主要内容,如果未能解决你的问题,请参考以下文章

使用 XmlSerializer 将空 xml 属性值反序列化为可为空的 int 属性

使用 XmlSerializer 将 XML 反序列化为类型

C# JSON 将文件反序列化为对象列表失败,并将字符串转换为集合错误

无法使用 c# xmlserializer 反序列化以前序列化的 XML

将 JSON 字符串反序列化为多个 C# 对象

c# XmlSerializer 反序列化器缺少默认命名空间