XML C# 中带有属性的可空元素的序列化
XML serialization of nillable elements with attributes in C#
我们使用属性 nilReason 来表达 XML 元素为空的原因。示例:
<dateOfDeath nilReason="noValue" xsi:nil="true"/>
<dateOfDeath nilReason="valueUnknown" xsi:nil="true"/>
在第一个例子中,这个人还活着,因为没有死亡日期。在第二个例子中,我们不知道死亡日期是什么。
该元素的XSD-定义如下:
<xs:element name="dateOfDeath" type="DateOfDeath" nillable="true"/>
<xs:complexType name="DateOfDeath">
<xs:simpleContent>
<xs:extension base="xs:date">
<xs:attribute name="nilReason" type="NilReason"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="NilReason">
<xs:restriction base="xs:string">
<xs:enumeration value="noValue"/>
<xs:enumeration value="valueUnknown"/>
</xs:restriction>
</xs:simpleType>
我 运行 使用 .net 框架提供的 XSD.exe 工具生成 C# 类 时遇到问题。如何编写生成以下内容的代码 XML?
<dateOfDeath nilReason="noValue" xsi:nil="true"/>
这是我能写出的最好的近似代码:
DateOfDeath dateOfDeath = new DateOfDeath();
dateOfDeath.nilReason = NilReason.noValue;
dateOfDeath.nilReasonSpecified = true;
XmlSerializer serializer = new XmlSerializer(typeof(DateOfDeath));
StreamWriter writer = new StreamWriter("dateofdeath.xml");
serializer.Serialize(writer, dateOfDeath);
writer.Close();
然而,不幸的是,这段代码产生了以下结果:
<dateOfDeath nilReason="noValue">0001-01-01</dateOfDeath>
这不是我想要的,因为它会生成一个虚拟日期值。看来这是序列化器的一个缺点。避免此问题的唯一方法似乎是应用一个函数,该函数删除虚拟值并在序列化后插入 xsi:nil="true" 属性。然后还需要一个在反序列化之前删除 xsi:nil="true" 属性的函数。否则nilReason-attribute的信息会在反序列化过程中被丢弃
问题是属性是在同一个 DateOfDeath 中与其值并排生成的 class(为简洁起见,我省略了一些代码):
public partial class DateOfDeath
{
private NilReason nilReasonField;
private bool nilReasonFieldSpecified;
private System.DateTime valueField;
[System.Xml.Serialization.XmlAttributeAttribute()]
public NilReason nilReason
{
get/set...
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool nilReasonSpecified
{
get/set...
}
[System.Xml.Serialization.XmlText(DataType = "date")]
public System.DateTime Value
{
get/set...
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.81.0")]
[System.SerializableAttribute()]
public enum NilReason
{
noValue,
valueUnknown,
}
因此,为了序列化一个 nil 元素,您必须将父元素设置为 null:
DateOfDeath dod = null;
serializer.Serialize(stream, dod);
生成如下内容:
<dateOfDeath xmlns:xsi="..." xmlns:xsd="..." xsi:nil="true" />
这当然会导致您无法设置属性:
DateOfDeath dod = null;
dod.nilReason = noValue; // does not work with nullpointer
然而,该值呈现为 xml 元素的文本,例如:
<dateOfDeath xmlns:xsi="..." xmlns:xsd="...">[value]</dateOfDeath>
其中 [value] 当然是您日期的文本表示。因此,即使您可以将值设置为 null - 因为您无法将复杂类型(例如 Nullable)呈现为 XmlText,所以您不能将其设置为 null - 您仍然无法将父 () 元素设置为 nil无论如何。
所以也许最接近您想要的是使值可为空并将其呈现为 XmlElement(注意添加的问号):
private System.DateTime? valueField;
[System.Xml.Serialization.XmlElement(DataType = "date", IsNullable = true)]
public System.DateTime? Value { get/set ...}
将其设置为空
DateOfDeath dod = new DateOfDeath();
dod.nilReason = NilReason.noValue;
dod.nilReasonSpecified = true;
dod.Value = null;
XmlSerializer serializer = new XmlSerializer(typeof(DateOfDeath));
serializer.Serialize(stream, dod);
给你:
<?xml version="1.0" encoding="utf-8"?>
<dateOfDeath xmlns:xsi="..." xmlns:xsd="..." nilReason="noValue">
<Value xsi:nil="true" />
</dateOfDeath>
这显然不是您想要的,但是除非有一种神奇的方法可以将外部 class 成员作为属性附加到空指针或相反,否则请使用 [=] 的另一个成员38=] 作为零值指标,使用给定的工具链不可能实现这一目标。
接下来的两个函数解决了这个问题。第一个 (addNilAttributes) 将属性 xsi:nil="true" 添加到包含属性 nilReason 的元素,并使元素为空。此函数必须在序列化后应用。
static public XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
static public XElement addNilAttributes(XElement root)
{
IEnumerable<XElement> noValueElements =
from el in root.Descendants()
where (string)el.Attribute("nilReason") != null
select el;
foreach (XElement el in noValueElements)
{
el.Add(new XAttribute(xsi + "nil", "true"));
el.ReplaceNodes(null); // make element empty
}
IEnumerable<XElement> nilElements =
from el in root.Descendants()
where (string)el.Attribute("nilReason") == null && (string)el.Attribute(xsi + "nil") != null
select el;
nilElements.Remove();
return root;
}
例如,<dateOfDeath nilReason="noValue">0001-01-01</dateOfDeath>
将被翻译成<dateOfDeath nilReason="noValue" xsi:nil="true"/>
。但是 <dateOfDeath xsi:nil="true"/>
将被删除,因为您总是必须指定 nilReason 以防元素为空。
第二个函数 (removeNilAttributes) 在反序列化之前删除 xsi:nil 属性。否则nilReason属性的值会在反序列化过程中丢失。
static public XElement removeNilAttributes(XElement root)
{
root.DescendantsAndSelf().Attributes(xsi + "nil").Remove();
return root;
}
比如<dateOfDeath nilReason="noValue" xsi:nil="true"/>
在反序列化之前会被转换成<dateOfDeath nilReason="noValue"/>
。
下面是如何应用这两个函数的示例代码:
DateOfDeath dateOfDeath = new DateOfDeath();
dateOfDeath.nilReason = NilReasonType.noValue;
dateOfDeath.nilReasonSpecified = true;
XmlSerializer serializer = new XmlSerializer(typeof(DateOfDeath));
StringWriter writer = new StringWriter();
serializer.Serialize(writer, dateOfDeath);
String str = writer.ToString();
Console.WriteLine(str);
writer.Close();
XElement root = XElement.Parse(str);
root = addNilAttributes(root);
Console.WriteLine(root.ToString());
root = removeNilAttributes(root);
Console.WriteLine(root.ToString());
StringReader reader = new StringReader(root.ToString());
DateOfDeath dateOfDeath2 = new DateOfDeath();
dateOfDeath2 = (DateOfDeath)serializer.Deserialize(reader);
输出:
<dateOfDeath xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="
tp://www.w3.org/2001/XMLSchema" nilReason="noValue">0001-01-01</dateOfDeath>
<dateOfDeath xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="
tp://www.w3.org/2001/XMLSchema" nilReason="noValue" xsi:nil="true"/>
<dateOfDeath xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="
tp://www.w3.org/2001/XMLSchema" nilReason="noValue"/>
我们使用属性 nilReason 来表达 XML 元素为空的原因。示例:
<dateOfDeath nilReason="noValue" xsi:nil="true"/>
<dateOfDeath nilReason="valueUnknown" xsi:nil="true"/>
在第一个例子中,这个人还活着,因为没有死亡日期。在第二个例子中,我们不知道死亡日期是什么。
该元素的XSD-定义如下:
<xs:element name="dateOfDeath" type="DateOfDeath" nillable="true"/>
<xs:complexType name="DateOfDeath">
<xs:simpleContent>
<xs:extension base="xs:date">
<xs:attribute name="nilReason" type="NilReason"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="NilReason">
<xs:restriction base="xs:string">
<xs:enumeration value="noValue"/>
<xs:enumeration value="valueUnknown"/>
</xs:restriction>
</xs:simpleType>
我 运行 使用 .net 框架提供的 XSD.exe 工具生成 C# 类 时遇到问题。如何编写生成以下内容的代码 XML?
<dateOfDeath nilReason="noValue" xsi:nil="true"/>
这是我能写出的最好的近似代码:
DateOfDeath dateOfDeath = new DateOfDeath();
dateOfDeath.nilReason = NilReason.noValue;
dateOfDeath.nilReasonSpecified = true;
XmlSerializer serializer = new XmlSerializer(typeof(DateOfDeath));
StreamWriter writer = new StreamWriter("dateofdeath.xml");
serializer.Serialize(writer, dateOfDeath);
writer.Close();
然而,不幸的是,这段代码产生了以下结果:
<dateOfDeath nilReason="noValue">0001-01-01</dateOfDeath>
这不是我想要的,因为它会生成一个虚拟日期值。看来这是序列化器的一个缺点。避免此问题的唯一方法似乎是应用一个函数,该函数删除虚拟值并在序列化后插入 xsi:nil="true" 属性。然后还需要一个在反序列化之前删除 xsi:nil="true" 属性的函数。否则nilReason-attribute的信息会在反序列化过程中被丢弃
问题是属性是在同一个 DateOfDeath 中与其值并排生成的 class(为简洁起见,我省略了一些代码):
public partial class DateOfDeath
{
private NilReason nilReasonField;
private bool nilReasonFieldSpecified;
private System.DateTime valueField;
[System.Xml.Serialization.XmlAttributeAttribute()]
public NilReason nilReason
{
get/set...
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool nilReasonSpecified
{
get/set...
}
[System.Xml.Serialization.XmlText(DataType = "date")]
public System.DateTime Value
{
get/set...
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.81.0")]
[System.SerializableAttribute()]
public enum NilReason
{
noValue,
valueUnknown,
}
因此,为了序列化一个 nil 元素,您必须将父元素设置为 null:
DateOfDeath dod = null;
serializer.Serialize(stream, dod);
生成如下内容:
<dateOfDeath xmlns:xsi="..." xmlns:xsd="..." xsi:nil="true" />
这当然会导致您无法设置属性:
DateOfDeath dod = null;
dod.nilReason = noValue; // does not work with nullpointer
然而,该值呈现为 xml 元素的文本,例如:
<dateOfDeath xmlns:xsi="..." xmlns:xsd="...">[value]</dateOfDeath>
其中 [value] 当然是您日期的文本表示。因此,即使您可以将值设置为 null - 因为您无法将复杂类型(例如 Nullable
所以也许最接近您想要的是使值可为空并将其呈现为 XmlElement(注意添加的问号):
private System.DateTime? valueField;
[System.Xml.Serialization.XmlElement(DataType = "date", IsNullable = true)]
public System.DateTime? Value { get/set ...}
将其设置为空
DateOfDeath dod = new DateOfDeath();
dod.nilReason = NilReason.noValue;
dod.nilReasonSpecified = true;
dod.Value = null;
XmlSerializer serializer = new XmlSerializer(typeof(DateOfDeath));
serializer.Serialize(stream, dod);
给你:
<?xml version="1.0" encoding="utf-8"?>
<dateOfDeath xmlns:xsi="..." xmlns:xsd="..." nilReason="noValue">
<Value xsi:nil="true" />
</dateOfDeath>
这显然不是您想要的,但是除非有一种神奇的方法可以将外部 class 成员作为属性附加到空指针或相反,否则请使用 [=] 的另一个成员38=] 作为零值指标,使用给定的工具链不可能实现这一目标。
接下来的两个函数解决了这个问题。第一个 (addNilAttributes) 将属性 xsi:nil="true" 添加到包含属性 nilReason 的元素,并使元素为空。此函数必须在序列化后应用。
static public XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
static public XElement addNilAttributes(XElement root)
{
IEnumerable<XElement> noValueElements =
from el in root.Descendants()
where (string)el.Attribute("nilReason") != null
select el;
foreach (XElement el in noValueElements)
{
el.Add(new XAttribute(xsi + "nil", "true"));
el.ReplaceNodes(null); // make element empty
}
IEnumerable<XElement> nilElements =
from el in root.Descendants()
where (string)el.Attribute("nilReason") == null && (string)el.Attribute(xsi + "nil") != null
select el;
nilElements.Remove();
return root;
}
例如,<dateOfDeath nilReason="noValue">0001-01-01</dateOfDeath>
将被翻译成<dateOfDeath nilReason="noValue" xsi:nil="true"/>
。但是 <dateOfDeath xsi:nil="true"/>
将被删除,因为您总是必须指定 nilReason 以防元素为空。
第二个函数 (removeNilAttributes) 在反序列化之前删除 xsi:nil 属性。否则nilReason属性的值会在反序列化过程中丢失。
static public XElement removeNilAttributes(XElement root)
{
root.DescendantsAndSelf().Attributes(xsi + "nil").Remove();
return root;
}
比如<dateOfDeath nilReason="noValue" xsi:nil="true"/>
在反序列化之前会被转换成<dateOfDeath nilReason="noValue"/>
。
下面是如何应用这两个函数的示例代码:
DateOfDeath dateOfDeath = new DateOfDeath();
dateOfDeath.nilReason = NilReasonType.noValue;
dateOfDeath.nilReasonSpecified = true;
XmlSerializer serializer = new XmlSerializer(typeof(DateOfDeath));
StringWriter writer = new StringWriter();
serializer.Serialize(writer, dateOfDeath);
String str = writer.ToString();
Console.WriteLine(str);
writer.Close();
XElement root = XElement.Parse(str);
root = addNilAttributes(root);
Console.WriteLine(root.ToString());
root = removeNilAttributes(root);
Console.WriteLine(root.ToString());
StringReader reader = new StringReader(root.ToString());
DateOfDeath dateOfDeath2 = new DateOfDeath();
dateOfDeath2 = (DateOfDeath)serializer.Deserialize(reader);
输出:
<dateOfDeath xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="
tp://www.w3.org/2001/XMLSchema" nilReason="noValue">0001-01-01</dateOfDeath>
<dateOfDeath xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="
tp://www.w3.org/2001/XMLSchema" nilReason="noValue" xsi:nil="true"/>
<dateOfDeath xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="
tp://www.w3.org/2001/XMLSchema" nilReason="noValue"/>