C# Xml 序列化程序将列表反序列化为 0 而不是 null
C# Xml Serializer deserializes list to count of 0 instead of null
我对 XmlSerializer
如何在幕后工作感到困惑。我有一个 class 将 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 中缺少 <Tests>
,则列表应保持为空,但如果存在(可能为空)节点 <Tests />
,则应分配列表。
您观察到的是引用可修改集合(例如 List<T>
的成员在反序列化开始时由 XmlSerializer
自动预分配。我不知道有任何地方记录了这种行为。它可能与 to , which explains that, since XmlSerializer
supports adding to get-only and pre-allocated collections 中描述的行为有关,如果预分配的集合 包含默认项 则反序列化的项将附加到它 - 可能重复内容。 Microsoft 可能只是选择在反序列化开始时预分配 所有 可修改集合作为实现此操作的最简单方法。
该答案的解决方法,即使用代理数组 属性,在这里也适用。由于无法追加数组,因此 XmlSerializer
必须累积所有值并在反序列化完成后将它们重新设置。但是,如果从未遇到过相关标记,XmlSerializer
显然不会开始累积值,因此不会调用数组 setter。这似乎阻止了您不想要的集合的默认预分配:
[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
仅在适当的时候分配。
我们在 google 搜索同一问题后来到这里。
我们最终做的是在反序列化后检查 Count == 0,并手动将 属性 设置为 null;
...
var varMyDeserializedClass = MyXmlSerializer.Deserialize(new StringReader(myInput));
if (varMyDeserializedClass.ListProperty.Count == 0)
{
varMyDeserializedClass.ListProperty = null;
}
...
这是一种成本低廉的解决方法,但提供了预期的结果并且有助于避免重构或重新设计。
当您将 [System.Xml.Serialization.XmlElement(IsNullable = true)]
应用于 属性 时,列表在反序列化后将为 null。
另一种可能性是使用“魔法”“指定”后缀:
public bool TestsSpecified {get;set;}
如果你有一个序列化的 field/property XXX 和一个布尔值 属性 XXXSpecified,那么 bool 属性 是根据是否设置了 main field/property .
我对 XmlSerializer
如何在幕后工作感到困惑。我有一个 class 将 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 中缺少 <Tests>
,则列表应保持为空,但如果存在(可能为空)节点 <Tests />
,则应分配列表。
您观察到的是引用可修改集合(例如 List<T>
的成员在反序列化开始时由 XmlSerializer
自动预分配。我不知道有任何地方记录了这种行为。它可能与 XmlSerializer
supports adding to get-only and pre-allocated collections 中描述的行为有关,如果预分配的集合 包含默认项 则反序列化的项将附加到它 - 可能重复内容。 Microsoft 可能只是选择在反序列化开始时预分配 所有 可修改集合作为实现此操作的最简单方法。
该答案的解决方法,即使用代理数组 属性,在这里也适用。由于无法追加数组,因此 XmlSerializer
必须累积所有值并在反序列化完成后将它们重新设置。但是,如果从未遇到过相关标记,XmlSerializer
显然不会开始累积值,因此不会调用数组 setter。这似乎阻止了您不想要的集合的默认预分配:
[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
仅在适当的时候分配。
我们在 google 搜索同一问题后来到这里。 我们最终做的是在反序列化后检查 Count == 0,并手动将 属性 设置为 null;
...
var varMyDeserializedClass = MyXmlSerializer.Deserialize(new StringReader(myInput));
if (varMyDeserializedClass.ListProperty.Count == 0)
{
varMyDeserializedClass.ListProperty = null;
}
...
这是一种成本低廉的解决方法,但提供了预期的结果并且有助于避免重构或重新设计。
当您将 [System.Xml.Serialization.XmlElement(IsNullable = true)]
应用于 属性 时,列表在反序列化后将为 null。
另一种可能性是使用“魔法”“指定”后缀:
public bool TestsSpecified {get;set;}
如果你有一个序列化的 field/property XXX 和一个布尔值 属性 XXXSpecified,那么 bool 属性 是根据是否设置了 main field/property .