繁体   English   中英

您如何使用XmlSerializer将字符串列表反序列化为CDATA

[英]How do you (de)serialize a list of strings as CDATA using XmlSerializer

我需要将字符串列表序列化为CDATA,并认为我将遵循如何使用XmlSerializer将字符串序列化为CDATA答案

它像序列化的魅力一样工作。 我的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保持为空; 设置器中的值计数为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);

我以为我会通过减少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();
}

到目前为止,对我来说似乎还行,这是一个更小的子类:)

尽管简单代理可以使用单个值,但是由于.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);
        }
    }
}

问题在于,序列化代码不仅会一次性获取并设置集合。 例如,在反序列化时,它要么创建一个新集合,要么获取已经在该属性上设置的集合,然后添加到该集合中。 如果您在此处创建了一个新集合,该集合是根据应用程序需要处理的“真实”集合计算得出的,则对计算出的集合所做的任何更改都不会反映在“真实”集合中。

要解决此问题,我在上面的代码中所做的就是将集合的所有权从“实际”集合转移到“代理”集合,然后再次转移,这取决于正在访问哪个集合属性。 仅在从一个属性切换到另一个属性时才产生所有权转移的成本,因此,连续访问应用程序中的“真实” 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时,这才有意义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM