我需要将字符串列表序列化为CDATA,并认为我会关注answer的How do you serialize a string as CDATA using XmlSerializer。
它就像一个序列化的魅力。我的XML文件看起来符合要求:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<tlist>
<item><![CDATA[First string]]></item>
<item><![CDATA[Second string]]></item>
</tlist>
</root>
但是反序列化不起作用。 TestList保持空白; setter中的值有0个。我错过了什么?
[XmlRootAttribute("root")]
public class TestConfig
{
public TestConfig()
{
TestList = new List<string>();
CdataList = new List<XmlCDataSection>();
}
[XmlIgnore]
public List<string> TestList { get; set; }
[XmlArray("tlist")]
[XmlArrayItem("item")]
public List<XmlCDataSection> CdataList
{
get { return TestList.Select(a => new XmlDocument().CreateCDataSection(a)).ToList(); }
set
{
TestList = value.Select(s => s.Value).ToList();
}
}
public void Save(string path)
{
var serializer = new XmlSerializer(GetType());
using (var stream = new StreamWriter(path))
{
serializer.Serialize(stream, this);
}
}
public static TestConfig Load(string path)
{
var serializer = new XmlSerializer(typeof(TestConfig));
using (var stream = new StreamReader(path))
{
return (TestConfig)serializer.Deserialize(stream);
}
}
}
执行:
var t = new TestConfig();
t.TestList.Add("First string");
t.TestList.Add("Second string");
t.Save(@"C:\Test\cdatatest.xml");
var r = TestConfig.Load(@"C:\Test\cdatatest.xml");
Console.WriteLine("Testlist size is {0}", r.TestList.Count);
答案 0 :(得分:1)
我以为我会改善&#34;关于softwariness通过减少XMLWriter子类的长度的答案。
/// <summary>
/// Custom XmlWriter.
/// Wraps up another XmlWriter to intercept string writes within
/// elements and writes them as CDATA instead.
/// </summary>
public class XmlCDataWriter : XmlTextWriter
{
public override void WriteString(string text)
{
if (WriteState == WriteState.Element)
{
WriteCData(text);
}
else
{
base.WriteString(text);
}
}
/// <summary>
/// Creates an instance of the XmlTextWriter class using the specified <see cref="T:System.IO.TextWriter"/>.
/// </summary>
/// <param name="w">The TextWriter to write to. It is assumed that the TextWriter is already set to the correct encoding. </param>
public XmlCDataWriter( [NotNull] TextWriter w ) : base( w )
{
}
}
然后它可以像StringBuffer一样使用:
using (StringWriter textWriter = new StringWriter())
{
XmlSerializer serializer = new XmlSerializer( typeof( ... ) );
serializer.Serialize(new XmlCDataWriter(textWriter), ... );
return textWriter.ToString();
}
对我来说似乎到目前为止工作,它是一个更小的子类:)
答案 1 :(得分:0)
虽然简单代理使用单个值,但由于.NET XML序列化机制的工作方式,您必须对集合进行更深入的代理:
[XmlRootAttribute("root")]
public class TestConfig
{
public TestConfig()
{
TestList = new List<string>();
}
private List<string> testList;
[XmlIgnore]
public List<string> TestList
{
get
{
if (this.testList == null)
{
var newCollection = new List<string>();
if (this.cdataList != null)
{
foreach (var x in this.cdataList)
{
newCollection.Add(x.Value);
}
}
this.testList = newCollection;
this.cdataList = null;
}
return this.testList;
}
set
{
this.testList = value;
this.cdataList = null;
}
}
private List<XmlCDataSection> cdataList;
[XmlArray("tlist")]
[XmlArrayItem("item")]
public List<XmlCDataSection> CdataList
{
get
{
if (this.cdataList == null)
{
var newCollection = new List<XmlCDataSection>();
if (this.testList != null)
{
foreach (var x in this.testList)
{
newCollection.Add(new XmlDocument().CreateCDataSection(x));
}
}
this.cdataList = newCollection;
this.testList = null;
}
return this.cdataList;
}
set
{
this.cdataList = value;
this.testList = null;
}
}
public void Save(string path)
{
var serializer = new XmlSerializer(GetType());
using (var stream = new StreamWriter(path))
{
serializer.Serialize(stream, this);
}
}
public static TestConfig Load(string path)
{
var serializer = new XmlSerializer(typeof(TestConfig));
using (var stream = new StreamReader(path))
{
return (TestConfig)serializer.Deserialize(stream);
}
}
}
问题在于序列化代码并不是一次性获取和设置集合。例如,在进行反序列化时,它会创建一个新集合,或者获取已在该属性上设置的集合,并添加到该集合中。如果你在这里创建了一个新的集合,那么从&#34; real&#34;您的应用程序需要处理的集合,然后对计算集合的任何更改都不会反映在&#34; real&#34;集合。
要解决这个问题,我在上面的代码中所做的就是从&#34;真实&#34;中转移集合的所有权。收集到&#34;代理&#34;集合,然后再返回,具体取决于正在访问的集合属性。转移所有权的成本仅在从一个房产转换到另一个房产时产生,因此连续访问&#34;真实的&#34;您应用程序中的TestList
集合不会产生这笔费用。
如果你有很多这样的收藏品,这有些不太优雅。如果您希望将所有元素文本序列化为CDATA,则可以实现自定义XmlWriter
,如下所示:
/// <summary>
/// Custom XmlWriter.
/// Wraps up another XmlWriter to intercept string writes within
/// elements and writes them as CDATA instead.
/// </summary>
public class XmlCDataWriter : XmlWriter
{
XmlWriter w;
public XmlCDataWriter(XmlWriter baseWriter)
{
this.w = baseWriter;
}
public override void Close()
{
w.Close();
}
public override void Flush()
{
w.Flush();
}
public override string LookupPrefix(string ns)
{
return w.LookupPrefix(ns);
}
public override void WriteBase64(byte[] buffer, int index, int count)
{
w.WriteBase64(buffer, index, count);
}
public override void WriteCData(string text)
{
w.WriteCData(text);
}
public override void WriteCharEntity(char ch)
{
w.WriteCharEntity(ch);
}
public override void WriteChars(char[] buffer, int index, int count)
{
w.WriteChars(buffer, index, count);
}
public override void WriteComment(string text)
{
w.WriteComment(text);
}
public override void WriteDocType(string name, string pubid, string sysid, string subset)
{
w.WriteDocType(name, pubid, sysid, subset);
}
public override void WriteEndAttribute()
{
w.WriteEndAttribute();
}
public override void WriteEndDocument()
{
w.WriteEndDocument();
}
public override void WriteEndElement()
{
w.WriteEndElement();
}
public override void WriteEntityRef(string name)
{
w.WriteEntityRef(name);
}
public override void WriteFullEndElement()
{
w.WriteFullEndElement();
}
public override void WriteProcessingInstruction(string name, string text)
{
w.WriteProcessingInstruction(name, text);
}
public override void WriteRaw(string data)
{
w.WriteRaw(data);
}
public override void WriteRaw(char[] buffer, int index, int count)
{
w.WriteRaw(buffer, index, count);
}
public override void WriteStartAttribute(string prefix, string localName, string ns)
{
w.WriteStartAttribute(prefix, localName, ns);
}
public override void WriteStartDocument(bool standalone)
{
w.WriteStartDocument(standalone);
}
public override void WriteStartDocument()
{
w.WriteStartDocument();
}
public override void WriteStartElement(string prefix, string localName, string ns)
{
w.WriteStartElement(prefix, localName, ns);
}
public override WriteState WriteState
{
get { return w.WriteState; }
}
public override void WriteString(string text)
{
if (WriteState == WriteState.Element)
{
w.WriteCData(text);
}
else
{
w.WriteString(text);
}
}
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
{
w.WriteSurrogateCharEntity(lowChar, highChar);
}
public override void WriteWhitespace(string ws)
{
w.WriteWhitespace(ws);
}
}
然后您将使用它如下:
var serializer = new XmlSerializer(...));
using (var cdataWriter = new XmlCDataWriter(XmlWriter.Create("somepath.xml")))
{
serializer.Serialize(cdataWriter, myDocumentObject);
}
同样,如果您想将所有内容都写为CDATA,这只是一个选择。