处理 - 元素 xsi:nil 与无元素

Processing - Element xsi:nil vs. no element

在 C# 中,当处理包含在模式中定义为 nillable="true"minOccurs="0" 的元素的 XML 时,区分 nil 元素和 nil 元素的最优雅的解决方案是什么省略元素?

用例是这样一种情况,其中服务接收 XML 片段,其中包含代表记录的所有已更改字段的元素,但 none 字段未更改。

例如,当记录从 { a: 1; b: 2; c: 3; } 更改为 { a: 1; b: 4; c: null } 时,服务可能会收到:

<change>
  <b>4</b>
  <c xsi:nil="true" />
</change>

当记录从 { a: 1; b: 2; c: 3; }(相同)更改为 { a: 1; b: 4; c: 3 }('c' 没有变化)时,服务可能会收到:

<change>
  <b>4</b>
</change>

然而,在 C# 中,这两个具有不同含义的片段都映射到一个看起来像 { a: null; b: 4; c: null; } 的对象。解析 XML 时,有关 c 明确为 nil 或根本不存在的信息将丢失。我们不确定 a 和 b 是应该设置为 null,还是保持不变。

在此示例中,您可能会建议消息应包含所有字段以避免混淆(以及用于标识正在更改的记录的内容),但我们正在处理有关大量记录的实际消息,需要只有发送实际才是相关的。而且我们处理的不仅仅是整数字段,还有各种简单和复杂的类型。

我认为 XML 片段相当优雅和清晰,但是在 C# 应用程序中处理它们时,您建议的最优雅和清晰的解决方案是什么?

使用 Linq-to-Xml 您可以解析字符串/流/文件并确定元素节点是否包含值。

XElement change = XElement.Parse(string); // .Load(stream or file)
var changes = change.Elements()
                    .Where(x => (string)x != null)
                    // The above Where() determines your empty from non-empty
                    .Select(x => 
                    { 
                        Name = x.Name.LocalName, 
                        Value = (string)x 
                    })
                    .ToList();

假设您正在使用 XmlSerializer,您可以添加一个额外的布尔值 属性 以记住是否已显式设置 属性。此外,如果 属性 的名称为 XXXSpecified,其中 XXX 是相关 "real" 属性 的名称,则 XmlSerializer 将省略 属性关于连载。例如:

public class TestClass
{
    string _value = null;

    [XmlElement("Value", IsNullable=true)]
    public string Value
    {
        get { return _value; }
        set
        {
            _value = value;
            ValueSpecified = true;
        }
    }

    [XmlIgnore]
    public bool ValueSpecified { get; set; }

    public static void Test()
    {
        Test(new TestClass());
        Test(new TestClass() { Value = null });
        Test(new TestClass() { Value = "Something" });
    }

    static void Test(TestClass test)
    {
        var xml = test.GetXml();
        Debug.WriteLine(xml);
        var testBack = xml.LoadFromXML<TestClass>();
        Debug.Assert(testBack.Value == test.Value && testBack.ValueSpecified == test.ValueSpecified);
    }
}

三个测试用例的XML输出是:

<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />

<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Value xsi:nil="true" />
</TestClass>

<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Value>Something</Value>
</TestClass>

如您所见,null 属性 和未设置 属性 之间的区别已成功序列化和反序列化。

有关详细信息,请参阅此处:MinOccurs 属性绑定支持 。 (该文档描述了对 public 字段 的支持,但该功能也适用于 public 属性。)