[英]Problems serializing a class to XML and including a CDATA section
I have a class that is serialized into XML for consumption by a web service.我有一个序列化为 XML 以供 Web 服务使用的类。 In this classes instance the XML must include a CDATA section for the web service to read it but I am at a loss on how to implement this.在这个类实例中,XML 必须包含一个 CDATA 部分供 Web 服务读取它,但我不知道如何实现它。
The XML needs to look like: XML 需要如下所示:
<UpdateOrderStatus>
<Action>2</Action>
<Value>
<![CDATA[
<Shipment>
<Header>
<SellerID>
...
]]>
</Value>
</UpdateOrderStatus>
I am able to generate the appropriate XML, except for the CDATA part.我能够生成适当的 XML,CDATA 部分除外。
My class structure looks like:我的班级结构如下:
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();
}
....
I have seen some suggestions on using:我看到了一些关于使用的建议:
[XmlElement("node", typeof(XmlCDataSection))]
but that causes an exception但这会导致异常
I have also tried我也试过
[XmlElement("Value" + "<![CDATA[")]
but the resulting XML is incorrect showing但生成的 XML 显示不正确
<Value_x003C__x0021__x005B_CDATA_x005B_>
....
</Value_x003C__x0021__x005B_CDATA_x005B_>
Can anyone show me what I am doing wrong, or where I need to go with this?任何人都可以告诉我我做错了什么,或者我需要去哪里吗?
--Edit-- - 编辑 -
making shipmentInfo serializable per carlosfigueira works for the most part, however I get extra?使每个 carlosfigueira 的 shipmentInfo 可序列化在大多数情况下都有效,但是我得到额外的? characters in the resulting XML ( see post Writing an XML fragment using XmlWriterSettings and XmlSerializer is giving an extra character for details )生成的 XML 中的字符(有关详细信息,请参阅使用 XmlWriterSettings 和 XmlSerializer 编写 XML 片段正在提供额外字符一文)
As such I changed the Write XML method to:因此,我将 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()));
}
}
}
However I am not getting an exception:但是我没有例外:
System.InvalidOperationException: There was an error generating the XML document. ---> System.ArgumentException: '.', hexadecimal
value 0x00, is an invalid character.
--Edit -- - 编辑 -
The exception was caused by the inclusion of my previous serializeToString method.异常是由于包含我以前的 serializeToString 方法引起的。 Since removing that the CDATA output is correct, except for a spacing issue, but I am also getting a namespace and xml declaration that should be removed by the XML settings specified.由于删除 CDATA 输出是正确的,除了间距问题,但我也得到了一个命名空间和 xml 声明,它们应该由指定的 XML 设置删除。 Output is:输出是:
<?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>
Any ideas of avoiding the BOM using the new class?使用新类避免 BOM 的任何想法?
--Edit 3 -- SUCCESS! --编辑 3 --成功!
I have implemented changes suggested below and now have the following writer class and test methods:我已经实施了下面建议的更改,现在有以下编写器类和测试方法:
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());
}
}
This produces the following XML:这会生成以下 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>
In response to the 'spaces' you are seeing after your edit, it is because of the encoding you are using (Unicode, 2 bytes per character).针对您在编辑后看到的“空格”,这是因为您使用的编码(Unicode,每个字符 2 个字节)。
Try:尝试:
settings.Encoding = new Utf8Encoding(false);
EDIT:编辑:
Also, note that format of the MemoryStream
is not necessarily a valid UTF-8 encoded string!另外,请注意MemoryStream
的格式不一定是有效的 UTF-8 编码字符串! You can use a StringBuilder
instead of MemoryStream
to create your inner writer.您可以使用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());
}
}
Could this be of any help: http://msdn.microsoft.com/en-us/library/system.xml.xmldocument.createcdatasection.aspx这有什么帮助吗: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);
Also, what Exception did you get when using the attribute?另外,您在使用该属性时得到了什么异常?
-- edit -- - 编辑 -
You could try adding a custom class, and do something like this:您可以尝试添加自定义类,然后执行以下操作:
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);
}
}
As found here http://bytes.com/topic/net/answers/530724-cdata-xmltextattribute在这里找到http://bytes.com/topic/net/answers/530724-cdata-xmltextattribute
Implementing ShipmentInfo as an IXmlSerializable
type will get close to what you need - see example below.将 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()));
}
}
The below example is only when the structure of the schema is defined and you have no choice of altering the schema.以下示例仅在定义架构的结构并且您无法选择更改架构时使用。
When you Deserialize/Serialize a [xmltext] value it is very difficult to hold the text in the CDATA[]
enclose.当您反序列化/序列化[xmltext]值时,很难将文本包含在CDATA[]
中。 you can use compiletransform to get the CDATA
value in the xml
as it is but the CDATA
is lost as soon as you deserialize in C# and load to memory stuff.您可以使用 compiletransform 来获取xml
中的CDATA
值,但是一旦您在 C# 中反序列化并加载到内存中, CDATA
就会丢失。
This is one of the easiest way to do这是最简单的方法之一
CDATA
to the test1 value.最终的 xml 可以转换为字符串并如下所示进行解析,并将其作为字符串返回,将CDATA
嵌入到 test1 值中。string xml ="<test><test1>@#@!#!#!@#!@#%$%@#$%#$%</test1></test>";
XNamespace ns = @"";
XDocument doc = XDocument.Parse(xml);
string xmlString = string.Empty;
var coll = from query in doc.Descendants(ns + "test1")
select query;
foreach (var value in coll){
value.ReplaceNodes(new XCData(value .Value));
}
doc.save("test.xml");// convert doc.tostring()
I think carlosfigueira just gave an elegant answer, but perhaps a little hard to understand at first sight.我认为carlosfigueira只是给出了一个优雅的答案,但乍一看可能有点难以理解。 Here is an alternative for your consideration, you can Serialize/Deserialize UpdateOrderStatus
and ShipmentInfo
separately.这是供您考虑的替代方案,您可以分别序列化/反序列化UpdateOrderStatus
和ShipmentInfo
。
Define your business object class as:将您的业务对象类定义为:
[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; }
}
Note that you should use XmlCDataSection
for the Value
field.请注意,您应该为Value
字段使用XmlCDataSection
。 Here is the test/helper functions:这是测试/帮助函数:
// 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);
}
}
...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.