2016-04-29 118 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架構進行驗證。此外,表達日期元素的新語法變得異乎尋常。但是仍然非常感謝你的分析,因爲它肯定使問題比我更清楚 –

相關問題