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

时间:2015-04-21 17:19:19

标签: c# xml xml-nil

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

用例是服务接收XML片段的情况,该片段包含表示已更改的记录的所有字段的元素,但没有任何字段未更改。

例如,当记录从{ 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 }时(&#39; c&#39;没有变化),该服务可能会收到:

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

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

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

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

2 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

假设您正在使用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属性和unset属性之间的区别已成功序列化和反序列化。

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