2016-04-29 106 views
2

我们使用属性nilReason来表示XML元素为空的原因。示例:C#中带有属性的nillable元素的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 Framework提供的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属性的信息将被丢弃。

+1

当然,'dateOfDeath =新DateOfDeath()' - 我吨不是空的。 –

+0

@AlexanderPetrov:的确,对象的dateOfDeath不为null,因为我必须设置属性如'dateOfDeath.noValue =“noValue”',它对应于XML属性“noValue”。但是我无法设置属性'dateOfDeath.Value = null',它对应于元素本身的空白内容。此问题是由生成的DateTime数据类型不可空行为造成的。我试图通过用DateTime替换DateTime的所有出现来解决此问题?在生成的代码中。但是,然后串行器产生一个错误。 –

+0

在一种情况下,您将收到'xsi:nil =“true”''DateOfDeath dateOfDeath = null;' –

回答

0

接下来的两个函数解决了这个问题。第一个(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"/>

2

的问题是,该属性由边侧产生与它的价值,在同DateOfDeath类(我离开了为简洁一些代码):

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> 

其中[值]当然的文本表示您日期。因此,即使您可以将值设置为空 - 您无法将该值设置为null,因为您无法将复杂类型(例如Nullable <DateTime>)渲染为XmlText - 但您仍然无法将父元素(<DateOfDeath>)元素设置为零无论如何。

所以,也许最接近你想要的是让值可为空,并使其作为的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> 

这显然不是你想要的,但除非有魔法将外部类成员作为属性附加到空指针或其他方式,将类的另一个成员作为nil值指示器使用的方法,那么使用给定的工具链就没有机会实现这一点。

+0

这实际上并不是我正在寻找的,因为在您的解决方案中,XML无法通过生成代码的原始XSD架构进行验证。此外,表达日期元素的新语法变得异乎寻常。但是仍然非常感谢你的分析,因为它肯定使问题比我更清楚 –

相关问题