处理 - 元素 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 属性。)
在 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 属性。)