你如何(DE)作为CDATA使用XmlSerializer的序列化的字符串列表 [英] How do you (de)serialize a list of strings as CDATA using XmlSerializer

查看:139
本文介绍了你如何(DE)作为CDATA使用XmlSerializer的序列化的字符串列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要序列化字符串作为CDATA的列表,并以为我会遵循回答How你使用序列化XmlSerializer的一个字符串作为CDATA。



它的工作原理像序列化的魅力。我的XML文件看起来期望:

 < XML版本=1.0编码=UTF-8>? 
<根系的xmlns:XSI =htt​​p://www.w3.org/2001/XMLSchema-instance的xmlns:XSD =htt​​p://www.w3.org/2001/XMLSchema>
<&TLIST GT;
<项目><![CDATA [第一个字符串]>< /项目>
<项目><![CDATA [第二个字符串]>< /项目>
< / TLIST>
< /根>



但反序列化不起作用。该TestList仍是空的;在二传值具有计数为0有什么我错过了吗?



  [XmlRootAttribute(根)] 
公类TestConfig
{
公共TestConfig()
{
TestList =新的List<串GT;();
CdataList =新的List< XmlCDataSection>();
}

[XmlIgnore]
公开名单<串GT; TestList {搞定;组; }

[XmlArray(TLIST)]
[XmlArrayItem(项目)]
公开名单< XmlCDataSection> CdataList
{
{返回TestList.Select(A =>新建的XmlDocument()CreateCDataSection(a)条。)了ToList()。 }

{
TestList = value.Select(S = GT; s.Value).ToList();
}
}

公共无效保存(字符串路径)
{
无功序列化=新的XmlSerializer(的GetType());使用(VAR流=新的StreamWriter(路径))
{
serializer.Serialize(流,这一点)
;
}
}

公共静态TestConfig负荷(字符串路径)
{
无功序列化=新的XmlSerializer(typeof运算(TestConfig));使用(VAR流=新的StreamReader(路径))
{
回报(TestConfig)serializer.Deserialize(流)
;
}
}
}



执行



  VAR T =新TestConfig(); 
t.TestList.Add(第一个字符串);
t.TestList.Add(第二个字符串);
t.Save(@C:\Test\cdatatest.xml);

变种R = TestConfig.Load(@C:\Test\cdatatest.xml);
Console.WriteLine(Testlist大小{0},r.TestList.Count);


解决方案

虽然简单代理与单个值工作,你必须要做收藏更深的代理,因为方式的.NET XML序列化机械工程:

  [XmlRootAttribute(根) ] 
公共类TestConfig
{
公共TestConfig()
{
TestList =新的List<串GT;();
}

私人列表<串GT; testList;

[XmlIgnore]
公开名单<串GT; TestList
{
得到
{
如果(this.testList == NULL)
{
变种newCollection =新的List<串>();

如果(this.cdataList!= NULL)
{
的foreach(在this.cdataList变种X)
{
newCollection.Add(X。值);
}
}

this.testList = newCollection;
this.cdataList = NULL;
}

返回this.testList;
}

{
this.testList =价值;
this.cdataList = NULL;
}
}

私人列表< XmlCDataSection> cdataList;

[XmlArray(TLIST)]
[XmlArrayItem(项目)]
公开名单< XmlCDataSection> CdataList
{
得到
{
如果(this.cdataList == NULL)
{
变种newCollection =新的List< XmlCDataSection>();

如果(this.testList!= NULL)
{
的foreach(VAR在this.testList X)
{
newCollection.Add(新的XmlDocument ().CreateCDataSection(X));
}
}

this.cdataList = newCollection;
this.testList = NULL;
}

返回this.cdataList;
}

{
this.cdataList =价值;
this.testList = NULL;
}
}

公共无效保存(字符串路径)
{
无功序列化=新的XmlSerializer(的GetType());使用(VAR流=新的StreamWriter(路径))
{
serializer.Serialize(流,这一点)
;
}
}

公共静态TestConfig负荷(字符串路径)
{
无功序列化=新的XmlSerializer(typeof运算(TestConfig));使用(VAR流=新的StreamReader(路径))
{
回报(TestConfig)serializer.Deserialize(流)
;
}
}
}



的问题是,序列化代码并不只是获取和一次性设定的集合。例如,当它的反序列化,它要么创建一个新的集合,或获取一个对属性已经设置,并增加了它。如果你已经创建了一个新的集合,从这里你的应用程序需要处理,然后计算收取任何更改将不会在真实收藏反映了真实的集合来计算。



要解决这个问题,我在上面的代码所做的就是从真正的集合转让集合所有权有关的代理集合,然后再返回,这取决于收集属性被访问。从一个属性切换到其他,所以接连访问你的应用程序将不会招致该费用真正的 TestList 集合时转移所有权的成本只是发生。



这是有些不雅,但如果你有很多这样的藏品。如果你想有序列化为CDATA所有元素的文字,你可以实现自定义的的XmlWriter ,如下所示:

  ///<总结> 
///自定义的XmlWriter。
///包装了另一个的XmlWriter截取字符串写入内
///元素,并将其写入为CDATA来代替。
///< /总结>
公共类XmlCDataWriter:的XmlWriter
{
的XmlWriter瓦;

公共XmlCDataWriter(XmlWriter的baseWriter)
{
this.w = baseWriter;
}

公共覆盖无效关闭()
{
w.Close();
}

公共覆盖无效的flush()
{
w.Flush();
}

公众覆盖字符串LookupPrefix(字符串NS)
{
返回w.LookupPrefix(NS);
}

公共覆盖无效WriteBase64(字节[]缓冲区,INT指数,诠释计数)
{
w.WriteBase64(缓冲,索引数);
}

公共覆盖无效WriteCData(字符串文本)
{
w.WriteCData(文本);
}

公共覆盖无效WriteCharEntity(CHAR CH)
{
w.WriteCharEntity(CH);
}

公共覆盖无效WriteChars(的char []缓冲区,INT指数,诠释计数)
{
w.WriteChars(缓冲,索引数);
}

公共覆盖无效WriteComment(字符串文本)
{
w.WriteComment(文本);
}

公共覆盖无效WriteDocType(字符串名称,字符串的PubID,字符串系统标识,串子集)
{
w.WriteDocType(姓名,发布商ID,系统标识,集);
}

公共覆盖无效WriteEndAttribute()
{
w.WriteEndAttribute();
}

公共覆盖无效WriteEndDocument()
{
w.WriteEndDocument();
}

公共覆盖无效对writeEndElement()
{
w.WriteEndElement();
}

公共覆盖无效WriteEntityRef(字符串名称)
{
w.WriteEntityRef(名);
}

公共覆盖无效WriteFullEndElement()
{
w.WriteFullEndElement();
}

公共覆盖无效WriteProcessingInstruction(字符串名称,字符串文本)
{
w.WriteProcessingInstruction(姓名,文字);
}

公共覆盖无效WriteRaw(字符串数据)
{
w.WriteRaw(数据);
}

公共覆盖无效WriteRaw(的char []缓冲区,INT指数,诠释计数)
{
w.WriteRaw(缓冲,索引数);
}

公共覆盖无效WriteStartAttribute(字符串前缀,字符串的localName,字符串NS)
{
w.WriteStartAttribute(前缀的localName,NS);
}

公共覆盖无效WriteStartDocument(布尔独立)
{
w.WriteStartDocument(独立);
}

公共覆盖无效WriteStartDocument()
{
w.WriteStartDocument();
}

公共覆盖无效WriteStartElement(字符串前缀,字符串的localName,字符串NS)
{
w.WriteStartElement(前缀的localName,NS);
}

公众覆盖WriteState WriteState
{
{返回w.WriteState; }
}

公共覆盖无效WriteString(字符串文本)
{
如果(WriteState == WriteState.Element)
{
W¯¯ .WriteCData(文本);
}
,否则
{
w.WriteString(文本);
}
}

公共覆盖无效WriteSurrogateCharEntity(CHAR lowChar,焦炭highChar)
{
w.WriteSurrogateCharEntity(lowChar,highChar);
}

公共覆盖无效WriteWhitespace(字符串WS)
{
w.WriteWhitespace(WS);
}
}

您会再使用它像如下:

  VAR串=新的XmlSerializer(...)); 
使用(VAR cdataWriter =新XmlCDataWriter(XmlWriter.Create(somepath.xml)))
{
serializer.Serialize(cdataWriter,myDocumentObject);
}



同样,这不仅使意义上的一个选项,如果你想要写的一切, CDATA。


I need to serialize a list of strings as CDATA and thought I would follow an answer of How do you serialize a string as CDATA using XmlSerializer.

It works like a charm for serializing. My XML file looks as desired:

<?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>

But deserialization does not work. The TestList remains empty; value in the setter has count 0. What have I missed?

[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);
    }
  }
}

Executing:

  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);

解决方案

Whilst simple proxies work with single values, you have to do a deeper proxying for collections, because of the way the .NET XML serialization machinery works:

[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);
        }
    }
}

The problem is that the serialization code doesn't just get and set the collections in one go. For example, when it's deserializing, it either creates a new collection, or gets one that's already set on the property, and adds to it. If you've created a new collection here computed from the "real" collection that your application needs to deal with, then any changes to the computed collection won't be reflected in the "real" collection.

To work around this, what I've done in the code above is to transfer ownership of the collection from the "real" collection to the "proxy" collection, and back again, depending on which collection property is being accessed. The cost of transferring ownership is incurred only when switching from one property to the other, so successive accesses to the "real" TestList collection in your application won't incur that expense.

This is somewhat inelegant though if you have many such collections. If you wanted to have all your element text serialized as CDATA, you could implement a custom XmlWriter, like the following:

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

You'd then use it like follows:

var serializer = new XmlSerializer(...));
using (var cdataWriter = new XmlCDataWriter(XmlWriter.Create("somepath.xml")))
{
    serializer.Serialize(cdataWriter, myDocumentObject);
}

Again, this only makes sense as an option if you want to write everything as CDATA.

这篇关于你如何(DE)作为CDATA使用XmlSerializer的序列化的字符串列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆