【问题标题】:How do you (de)serialize a list of strings as CDATA using XmlSerializer如何使用 XmlSerializer 将字符串列表(反)序列化为 CDATA
【发布时间】:2015-03-18 16:20:43
【问题描述】:

我需要将字符串列表序列化为 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 保持为空;设置器中的值计数为 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);

【问题讨论】:

    标签: c# serialization xml-serialization


    【解决方案1】:

    我想我会通过减少 XMLWriter 子类的长度来“改进”softwariness 的答案。

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

    到目前为止似乎对我有用,而且它是一个小得多的子类:)

    【讨论】:

    • 是否有一种紧凑的方法可以做到这一点并且仍然允许使用 XmlTextWriter XmlWriterSettings 选项?似乎 XmlWriterSettings 只能通过 XmlTextWriter.Create() 静态方法获得。它返回一个 XmlWriter 实例。
    • 没关系...似乎 XmlWriter 已经包含一个 XmlWriterSettings 成员变量,所以我只是添加了另一个构造函数,该构造函数采用 XmlWriterSettings 实例并深度复制了我关心的所有内容。跨度>
    【解决方案2】:

    虽然简单的代理可以处理单个值,但由于 .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 时,这才有意义。

    【讨论】:

      猜你喜欢
      • 2021-07-16
      • 2010-09-28
      • 1970-01-01
      • 2019-03-13
      • 1970-01-01
      • 2021-01-08
      • 2020-05-20
      相关资源
      最近更新 更多