2012-07-13 27 views
2

我有一個類被序列化爲XML供Web服務使用。在這個類的實例中,XML必須包含CDATA部分以供Web服務讀取,但是我對如何實現這一點不知所措。將類序列化爲XML幷包含CDATA段時的問題

的XML需要看起來像:

<UpdateOrderStatus> 
    <Action>2</Action> 
     <Value> 
      <![CDATA[ 
       <Shipment> 
        <Header> 
         <SellerID> 
          ... 
      ]]> 
     </Value> 
</UpdateOrderStatus> 

我能夠生成相應的XML,除了CDATA部分。

我的階級結構是這樣的:

public class UpdateOrderStatus 
{ 
    public int Action { get; set; } 


    public ValueInfo Value { get; set; } 

    public UpdateOrderStatus() 
    { 
     Value = new ValueInfo(); 
    } 


    public class ValueInfo 
    { 
     public ShipmentInfo Shipment { get; set; } 

     public ValueInfo() 
     { 
      Shipment = new ShipmentInfo(); 
     } 

     public class ShipmentInfo 
     { 
      public PackageListInfo PackageList { get; set; } 
      public HeaderInfo Header { get; set; } 
      public ShipmentInfo() 
      { 
       PackageList = new PackageListInfo(); 
       Header = new HeaderInfo(); 
      } 

     .... 

我看到的使用提出了一些建議:

[XmlElement("node", typeof(XmlCDataSection))] 

但導致異常

我也曾嘗試

[XmlElement("Value" + "<![CDATA[")] 

但是生成的XML是不正確顯示

<Value_x003C__x0021__x005B_CDATA_x005B_> 
.... 
</Value_x003C__x0021__x005B_CDATA_x005B_> 

誰能告訴我什麼,我做錯了,或者我需要去與此?

- 編輯 -

使每carlosfigueira shipmentInfo序列化工作的大部分,但我得到額外的?在生成的XML字符(請參閱發佈Writing an XML fragment using XmlWriterSettings and XmlSerializer is giving an extra character瞭解詳細信息)

因此我改變了寫XML的方法:

public void WriteXml(XmlWriter writer) 
     { 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
       ns.Add("", ""); 

       XmlWriterSettings settings = new XmlWriterSettings(); 

       settings.OmitXmlDeclaration = true; 
       settings.Encoding = new UnicodeEncoding(bigEndian: false, byteOrderMark: false); 
       settings.Indent = true; 

       using (XmlWriter innerWriter = XmlWriter.Create(ms, settings)) 
       { 
        shipmentInfoSerializer.Serialize(innerWriter, this.Shipment,ns); 
        innerWriter.Flush(); 
        writer.WriteCData(Encoding.UTF8.GetString(ms.ToArray())); 
       } 
      } 
     } 

但是我沒有得到一個例外:

System.InvalidOperationException: There was an error generating the XML document. ---> System.ArgumentException: '.', hexadecimal 
value 0x00, is an invalid character. 

- 編輯 -

異常是由於包含我以前的serializeToString方法造成的。由於刪除了CDATA輸出是正確的,除了空格問題,但我也得到了一個名稱空間和xml聲明,應該通過指定的XML設置來刪除它。輸出是:

<?xml version="1.0"?> 
<UpdateOrderStatus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Action>1</Action> 
    <Value><![CDATA[< S h i p m e n t I n f o > 
    < P a c k a g e L i s t > 
     < P a c k a g e > 
      < S h i p D a t e > 2 0 1 2 - 0 7 - 1 3 T 1 1 : 5 8 : 5 1 . 0 9 2 5 6 1 5 - 0 4 : 0 0 </S h i p D a t e > 
      < I t e m L i s t > 
       < I t e m > 
        < S h i p p e d Q t y > 0 </S h i p p e d Q t y > 
       </I t e m > 
      </I t e m L i s t > 
     </P a c k a g e > 
    </P a c k a g e L i s t > 
    < H e a d e r > 
     < S e l l e r I d > S h i p m e n t h e a d e r </S e l l e r I d > 
     < S O N u m b e r > 0 </S O N u m b e r > 
    </H e a d e r > 
</S h i p m e n t I n f o > ]]></Value> 
</UpdateOrderStatus> 

任何避免BOM使用新類的想法?

- 編輯3 - SUCCESS!

我已經實現了以下建議的修改,現在有下列作家類和測試方法:

UpdateOrderStatus obj = new UpdateOrderStatus(); 

     obj.Action = 1; 
     obj.Value = new UpdateOrderStatus.ValueInfo(); 
     obj.Value.Shipment = new UpdateOrderStatus.ValueInfo.ShipmentInfo(); 
     obj.Value.Shipment.Header.SellerId = "Shipment header"; 
     obj.Value.Shipment.PackageList = new UpdateOrderStatus.ValueInfo.ShipmentInfo.PackageListInfo(); 
     obj.Value.Shipment.PackageList.Package = new UpdateOrderStatus.ValueInfo.ShipmentInfo.PackageListInfo.PackageInfo(); 
     obj.Value.Shipment.PackageList.Package.ShipDate = DateTime.Now; 



     XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
     ns.Add("", ""); 
     XmlWriterSettings settings = new XmlWriterSettings(); 
     settings.OmitXmlDeclaration = true; 
     settings.Encoding = new UTF8Encoding(false); 
     settings.Indent = true; 
     XmlSerializer xs = new XmlSerializer(typeof(UpdateOrderStatus)); 
     MemoryStream ms = new MemoryStream(); 


     XmlWriter writer = XmlWriter.Create(ms, settings); 
     xs.Serialize(writer, obj, ns); 
     Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray())); 
    } 


public void WriteXml(XmlWriter writer) 
     { 

      XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
      ns.Add("", ""); 

      XmlWriterSettings settings = new XmlWriterSettings(); 

      settings.OmitXmlDeclaration = true; 
      settings.Indent = true; 

      StringBuilder sb = new StringBuilder(); 
      using (XmlWriter innerWriter = XmlWriter.Create(sb, settings)) 
      { 
       shipmentInfoSerializer.Serialize(innerWriter, this.Shipment, ns); 
       innerWriter.Flush(); 
       writer.WriteCData(sb.ToString()); 
      } 
     } 

這將產生以下XML:

<UpdateOrderStatus> 
    <Action>1</Action> 
    <Value><![CDATA[<ShipmentInfo> 
    <PackageList> 
    <Package> 
     <ShipDate>2012-07-13T14:05:36.6170802-04:00</ShipDate> 
     <ItemList> 
     <Item> 
      <ShippedQty>0</ShippedQty> 
     </Item> 
     </ItemList> 
    </Package> 
    </PackageList> 
    <Header> 
    <SellerId>Shipment header</SellerId> 
    <SONumber>0</SONumber> 
    </Header> 
</ShipmentInfo>]]></Value> 
</UpdateOrderStatus> 
+0

請注意,CDATA節是一種轉義xml的方式,使其更具可讀性。 CDATA的內容不是Xml。 <![CDATA []]>是<測試/ >的等效如果您是確保內容始終是你應該能夠預先處理文件刪除CDATA有效的XML文檔和反轉義CDATA節的內容。請注意,如果內容無效,Xml將使整個文檔無效。另一個選擇是如下所述實現IXmlSerializable,但一旦啓動它將會增長並且難以維護。 – Pawel 2012-07-13 16:44:08

回答

4

爲了響應您在編輯後看到的'空格',這是因爲您正在使用的編碼(Unicode,每個字符2個字節)。

嘗試:

settings.Encoding = new Utf8Encoding(false); 

編輯:

另外,還要注意的MemoryStream的該格式是一定是有效的UTF-8編碼的字符串!您可以使用StringBuilder而不是MemoryStream來創建您的內部作者。

public void WriteXml(XmlWriter writer) 
    { 
     XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
     ns.Add("", ""); 

     XmlWriterSettings settings = new XmlWriterSettings(); 

     settings.OmitXmlDeclaration = true; 
     settings.Indent = true; 

     StringBuilder sb = new StringBuilder(); 
     using (XmlWriter innerWriter = XmlWriter.Create(sb, settings)) 
     { 
      shipmentInfoSerializer.Serialize(innerWriter, this.Shipment,ns); 
      innerWriter.Flush(); 
      writer.WriteCData(sb.ToString()); 
     } 
    } 
+0

好,在閱讀整個文檔您鏈接到,我的行「XmlWriter作家= XmlWriter.Create(毫秒,設置);」應該保留在設置中列出的編碼,因爲我沒有指定編碼任何其他地方,但是當我執行時,我得到一個異常:「System.InvalidOperationException:生成XML文檔時發生錯誤---> System.ArgumentException:' ',十六進制爲 ,值爲0x00,是一個無效的字符。「 – 2012-07-13 16:51:10

+1

@RobertH我想這是因爲'MemoryStream'內容不一定是一個有效的UTF8編碼的字符串。我已經更新了我的答案。 – 2012-07-13 17:07:43

2

難道這是任何幫助:http://msdn.microsoft.com/en-us/library/system.xml.xmldocument.createcdatasection.aspx

//Create a CData section. 
XmlCDataSection CData; 
CData = doc.CreateCDataSection("All Jane Austen novels 25% off starting 3/23!");  

//Add the new node to the document. 
XmlElement root = doc.DocumentElement; 
root.AppendChild(CData); 

Console.WriteLine("Display the modified XML...");   
doc.Save(Console.Out); 

此外,什麼異常你在使用屬性時會得到什麼?

- 編輯 -

你可以嘗試添加自定義類,做這樣的事情:

some xml serializable class, 
{ 
    ....... 

    [XmlElement("PayLoad", Type=typeof(CDATA))] 
    public CDATA PayLoad 
    { 
     get { return _payLoad; } 
     set { _payLoad = value; } 
    } 
} 


public class CDATA : IXmlSerializable 
{ 
    private string text; 
    public CDATA() 
    {} 

    public CDATA(string text) 
    { 
     this.text = text; 
    } 

    public string Text 
    { 
     get { return text; } 
    } 

    /// <summary> 
    /// Interface implementation not used here. 
    /// </summary> 
    XmlSchema IXmlSerializable.GetSchema() 
    { 
     return null; 
    } 

    /// <summary> 
    /// Interface implementation, which reads the content of the CDATA tag 
    /// </summary> 
    void IXmlSerializable.ReadXml(XmlReader reader) 
    { 
     this.text = reader.ReadElementString(); 
    } 

    /// <summary> 
    /// Interface implementation, which writes the CDATA tag to the xml 
    /// </summary> 
    void IXmlSerializable.WriteXml(XmlWriter writer) 
    { 
     writer.WriteCData(this.text); 
    } 
} 

由於這裏找到http://bytes.com/topic/net/answers/530724-cdata-xmltextattribute

+0

我得到異常:System.InvalidOperationException:無法生成臨時類(結果= 1)。 – 2012-07-13 14:17:29

+0

嘗試把這樣的屬性[XmlElement(「CDataElement」)] – 2012-07-13 14:19:52

+0

當我這樣做時,它將重命名爲CDataElement,這將無法通過Web服務驗證 – 2012-07-13 14:22:13

2

實施ShipmentInfo爲IXmlSerializable類型將得到接近你所需要的 - 見下面的例子。

public class StackOverflow_11471676 
{ 
    public class UpdateOrderStatus 
    { 
     public int Action { get; set; } 
     public ValueInfo Value { get; set; } 
    } 
    [XmlType(TypeName = "Shipment")] 
    public class ShipmentInfo 
    { 
     public string Header { get; set; } 
     public string Body { get; set; } 
    } 
    public class ValueInfo : IXmlSerializable 
    { 
     public ShipmentInfo Shipment { get; set; } 
     private XmlSerializer shipmentInfoSerializer = new XmlSerializer(typeof(ShipmentInfo)); 

     public System.Xml.Schema.XmlSchema GetSchema() 
     { 
      return null; 
     } 

     public void ReadXml(XmlReader reader) 
     { 
      using (MemoryStream ms = new MemoryStream(
       Encoding.UTF8.GetBytes(
        reader.ReadContentAsString()))) 
      { 
       Shipment = (ShipmentInfo)this.shipmentInfoSerializer.Deserialize(ms); 
      } 
     } 

     public void WriteXml(XmlWriter writer) 
     { 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       using (XmlWriter innerWriter = XmlWriter.Create(ms, new XmlWriterSettings { OmitXmlDeclaration = true })) 
       { 
        shipmentInfoSerializer.Serialize(innerWriter, this.Shipment); 
        innerWriter.Flush(); 
        writer.WriteCData(Encoding.UTF8.GetString(ms.ToArray())); 
       } 
      } 
     } 
    } 
    public static void Test() 
    { 
     UpdateOrderStatus obj = new UpdateOrderStatus 
     { 
      Action = 1, 
      Value = new ValueInfo 
      { 
       Shipment = new ShipmentInfo 
       { 
        Header = "Shipment header", 
        Body = "Shipment body" 
       } 
      } 
     }; 

     XmlSerializer xs = new XmlSerializer(typeof(UpdateOrderStatus)); 
     MemoryStream ms = new MemoryStream(); 
     xs.Serialize(ms, obj); 
     Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray())); 
    } 
} 
+0

所以我做了一些修改爲你的代碼幾乎做什麼,我需要它 - 我需要刪除一些額外的?字符(見我的另一篇文章),但它拋出一個異常。請參閱編輯以獲取更多信息。 – 2012-07-13 15:42:24

+0

要刪除的BOM,設置XmlWriterSettings的一個實例的'Encoding'財產沒有BOM:'新XmlWriterSettings {OmitXmlDeclaration = TRUE,編碼=新的UTF8Encoding(假)}' – carlosfigueira 2012-07-13 17:20:56

0

我覺得carlosfigueira只是給了一個優雅的答案,但也許有點難以一見鍾情理解。這裏有一個替代方案供您考慮,您可以分別序列化或反序列化UpdateOrderStatusShipmentInfo

定義你的業務對象類:

[XmlRoot("UpdateOrderStatus")] 
    public class UpdateOrderStatus 
    { 
     [XmlElement("Action")] 
     public int Action { get; set; } 

     private String valueField; 
     [XmlElement("Value")] 
     public XmlCDataSection Value 
     { 
      get 
      { 
       XmlDocument xmlDoc = new XmlDocument(); 
       return xmlDoc.CreateCDataSection(valueField); 
      } 
      set 
      { 
       this.valueField = value.Value; 
      } 
     } 

     [XmlIgnore] 
     public ShipmentInfo Shipment 
     { 
      get; 
      set; 
     } 
    } 

    [XmlRoot("ShipmentInfo")] 
    public class ShipmentInfo 
    { 
     [XmlElement("Package")] 
     public String Package { get; set; } 
     [XmlElement("Header")] 
     public String Header { get; set; } 
    } 

請注意,您應該使用XmlCDataSectionValue領域。 下面是測試/輔助功能:例如低於

// Test function 
    const string t = @"<UpdateOrderStatus> 
     <Action>2</Action> 
      <Value> 
       <![CDATA[<ShipmentInfo> 
     <Package>packageInfo</Package> 
     <Header>headerInfo</Header> 
    </ShipmentInfo>]]> 
      </Value> 
    </UpdateOrderStatus>"; 

    static void Test1() 
    { 
     UpdateOrderStatus os = Deserialize(t); 

     String t2 = XmlUtil.Serialize(os); 
    } 

    // Helper functions 
    static UpdateOrderStatus Deserialize(String str) 
    { 
     UpdateOrderStatus os = XmlUtil.DeserializeString<UpdateOrderStatus>(str); 
     os.Shipment = XmlUtil.DeserializeString<ShipmentInfo>(os.Value.InnerText); 
     return os; 
    } 

    public static class XmlUtil 
    { 
     public static String Serialize<T>(T o) 
     { 
      XmlSerializer s = new XmlSerializer(typeof(T)); //, overrides); 
      //StringBuilder builder = new StringBuilder(); 

      MemoryStream ms = new MemoryStream(); 
      XmlWriterSettings settings = new XmlWriterSettings(); 
      settings.Encoding = Encoding.UTF8; 
      settings.Indent = true; 
      using (XmlWriter xmlWriter = XmlWriter.Create(ms, settings)) 
      { 
       XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
       ns.Add(String.Empty, String.Empty); 
       s.Serialize(xmlWriter, o, ns); 
      } 
      Byte[] bytes = ms.ToArray(); 
      // discard the BOM part 
      Int32 idx = settings.Encoding.GetPreamble().Length; 
      return Encoding.UTF8.GetString(bytes, idx, bytes.Length - idx); 
     } 

     public static T DeserializeString<T>(String content) 
     { 
      using (TextReader reader = new StringReader(content)) 
      { 
       XmlSerializer s = new XmlSerializer(typeof(T)); 
       return (T)s.Deserialize(reader); 
      } 
     } 

     ... 
    } 
1

的是模式的結構只被定義時,你沒有選擇改變的模式的。

當您反序列化/序列化[xmltext]值時,很難將文本保存在CDATA [] 中。你可以使用compiletransform來獲取xml中的CDATA值,但是一旦你在C#中反序列化並加載到內存中,CDATA就會丟失。

這是做

1的最簡單的方法之一)反序列化/序列化 2)一旦最終的XML輸出導出。 最終的xml可以將 轉換爲字符串並進行解析,如下所示,並返回它作爲字符串將CDATA嵌入到test1值。

字符串XML = 「@#@#@#@#%$%@#$%#$%!!!!」;

XNamespace ns = @「」;

XDocument doc = XDocument.Parse(xml);

string xmlString = string.Empty;

VAR COLL =從doc.Descendants(NS + 「測試1」) 選擇查詢查詢;

的foreach(在COLL VAR值){

value.ReplaceNodes(新XCData(值。價值));}

doc.save( 「的test.xml」); //轉換文檔。 tostring()