将类序列化为XML并包括CDATA部分时出现问题

我有一个序列化为XML的类供Web服务使用。 在这个类实例中,XML必须包含一个CDATA部分供Web服务读取,但我对如何实现它感到茫然。

XML需要如下所示:

 2  <![CDATA[  
... ]]>

我能够生成适当的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显示不正确

   ....  

任何人都可以告诉我我做错了什么,或者我需要去哪里?

– 编辑 –

制作shippingInfo序列化每carlosfigueira大部分工作,但我得到额外的? 生成的XML中的字符(请参阅使用XmlWriterSettings和XmlSerializer编写XML片段以获取详细信息的额外字符 )

因此我将Write 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. 

– 编辑 –

该exception是由于我之前的serializeToString方法的包含引起的。 由于删除了CDATA输出是正确的,除了间距问题,但我也得到了一个名称空间和xml声明,应该通过指定的XML设置删除。 输出是:

   1 <![CDATA[ 

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 0

S hipmentheader 0
]]>

有没有使用新类避免BOM的想法?

– 编辑3 – 成功!

我已经实现了下面建议的更改,现在有以下编写器类和测试方法:

  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:

  1 <![CDATA[   2012-07-13T14:05:36.6170802-04:00   0     
Shipment header 0
]]>

为了响应您在编辑后看到的“空格”,这是因为您正在使用的编码(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()); } } 

这可能有任何帮助: 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); 

另外,使用属性时你得到了什么Exception?

– 编辑 –

您可以尝试添加自定义类,并执行以下操作:

 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; } } ///  /// Interface implementation not used here. ///  XmlSchema IXmlSerializable.GetSchema() { return null; } ///  /// Interface implementation, which reads the content of the CDATA tag ///  void IXmlSerializable.ReadXml(XmlReader reader) { this.text = reader.ReadElementString(); } ///  /// Interface implementation, which writes the CDATA tag to the xml ///  void IXmlSerializable.WriteXml(XmlWriter writer) { writer.WriteCData(this.text); } } 

在此处找到http://bytes.com/topic/net/answers/530724-cdata-xmltextattribute

将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())); } } 

以下示例仅在定义了架构的结构且您无法更改架构时。

当您反序列化/序列化[xmltext]值时,很难将文本保存在CDATA []括起来。 您可以使用compiletransform来获取xml中的CDATA值,但是一旦在C#中反序列化并加载到内存中,CDATA就会丢失。

这是最简单的方法之一

1)反序列化/序列化2)一旦导出最终的xml输出。 最终的xml可以转换为字符串并进行解析,如下所示,并将其作为字符串返回,将CDATA嵌入test1值。

string xml =“@#@!#!#!@#!@#%$%@#$%#$%”;

XNamespace ns = @“”;

XDocument doc = XDocument.Parse(xml);

string xmlString = string.Empty;

var coll = from doc.Descendants(ns +“test1”)中的查询选择查询;

foreach(coll中的var值){

value.ReplaceNodes(new XCData(value .Value));}

doc.save(“test.xml”); //转换doc.tostring()

我认为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; } } 

请注意,您应该使用XmlCDataSection作为Value字段。 这是测试/帮助函数:

  // Test function const string t = @" 2   packageInfo 
headerInfo
]]>
"; static void Test1() { UpdateOrderStatus os = Deserialize(t); String t2 = XmlUtil.Serialize(os); } // Helper functions static UpdateOrderStatus Deserialize(String str) { UpdateOrderStatus os = XmlUtil.DeserializeString(str); os.Shipment = XmlUtil.DeserializeString(os.Value.InnerText); return os; } public static class XmlUtil { public static String Serialize(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(String content) { using (TextReader reader = new StringReader(content)) { XmlSerializer s = new XmlSerializer(typeof(T)); return (T)s.Deserialize(reader); } } ... }