2012-07-13 62 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()