你如何(de)使用XmlSerializer将字符串列表序列化为CDATA

时间:2015-03-18 16:20:44

标签: c# serialization xml-serialization

我需要将字符串列表序列化为CDATA,并认为我会关注answerHow 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);

2 个答案:

答案 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,这只是一个选择。