如何序列化的ICollection< T>还具有读/写属性来XML [英] How to serialize an ICollection<T> that also has read/write properties to XML

查看:182
本文介绍了如何序列化的ICollection< T>还具有读/写属性来XML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有实现自定义类的列表类。这一类也有两个属性。但是,当我序列化类,XML只包含我的自定义类的数组,但不包含其他两个属性。
这里是类:

 公共类Porudzbina:列表< PorudzbenicaStavka>中的IEnumerable<的SqlDataRecord> 
{
众长KomSifra {搞定;组; }
公众的Guid KomId {搞定;组; }

&IEnumerator的LT;的SqlDataRecord> IEnumerable的<的SqlDataRecord> .GetEnumerator()
{
变种sqlRow =新的SqlDataRecord(
新SqlMetaData(RB,SqlDbType.Int),
新SqlMetaData(RobaSifra SqlDbType.NVarChar,50),
新SqlMetaData(RobaNaziv,SqlDbType.NVarChar,100)
);
的foreach(在此PorudzbenicaStavka POR)
{
sqlRow.SetInt32(0,por.rb);
sqlRow.SetString(1,por.RobaSifra);
sqlRow.SetString(2,por.RobaNaziv);
收益率的回报sqlRow;
}
}
}

和代码,我用它来序列化:

  XmlSerializer的序列化=新的XmlSerializer(typeof运算(Porudzbina)); 
使用(TextWriter的作家=新的StreamWriter(@C:\Xmle.xml))
{
serializer.Serialize(作家,POR);
}



这是XML,我得到了:

 <?XML版本=1.0编码=UTF-8>?; 
< ArrayOfPorudzbenicaStavka的xmlns:XSI =htt​​p://www.w3.org/2001/XMLSchema-instance的xmlns:XSD =htt​​p://www.w3.org/2001/XMLSchema>
< PorudzbenicaStavka>
< RB> 1 LT; / RB>
< RobaSifra> 3702< / RobaSifra>
< RobaNaziv>富隆mlecna cokolada 33%,厄瓜多尔百克< / RobaNaziv>
< / PorudzbenicaStavka>
< PorudzbenicaStavka>
< RB> 2'; / RB>
< RobaSifra> 1182< / RobaSifra>
< RobaNaziv> IL卡皮塔诺zelena maslina SA paprikom720克< / RobaNaziv>
< / PorudzbenicaStavka>
< PorudzbenicaStavka>
< RB>第3版; / RB>
< RobaSifra> 1120 LT; / RobaSifra>
< RobaNaziv>凯泽金枪鱼排SA papricicomüulju170克< / RobaNaziv>
< / PorudzbenicaStavka>
< / ArrayOfPorudzbenicaStavka>



我想我的XML包含两个特性以及自定义类的数组,我可以反序列化到其原始状态...


解决方案

这是你的属性不反序列化的文档部分的序列化,实现一类ICollection接口:




您可以通过实现ICollection接口创建自己的集合类,并使用XmlSerializer的序列化这些类的实例。请注意,当一个类实现ICollection接口,只能由类包含在集合序列化。 的任何公共属性或字段添加到不会被序列化的类。




所以,就是这样。



您可能会考虑更改您的设计让你的类不具备的特性。由于某些原因使这一变化,请参见为什么不从列表继承?



如果您仍然选择与序列化的特性集合,你会需要的手动执行的IXmlSerializable 。这是沉重的负担,因为你需要处理很多边缘的情况下,包括空元素,意想不到的元素,注释,以及是否存在空白,这些都可以甩开你的的ReadXml()方法。对于一些背景,请参见如何实现IXmlSerializable的正确的。



首先,创建一个与序列化特性泛型列表的基类:

 公共类XmlSerializableList< T> :列表< T> ;,的IXmlSerializable其中T:新的()
{
公共XmlSerializableList():基地(){}

公共XmlSerializableList(IEnumerable的< T>集合):基地(集合){}

公共XmlSerializableList(INT容量):基地(容量){}

#地区的IXmlSerializable会员

常量字符串CollectionItemsName =项目;
常量字符串CollectionPropertiesName =属性;

无效IXmlSerializable.WriteXml(XmlWriter的作家)
{
//不要写的包装元素。

//序列化集合。
WriteCollectionElements(作家);

//序列化自定义属性。
writer.WriteStartElement(CollectionPropertiesName);
WriteCustomElements(作家);
writer.WriteEndElement();

//不要结束包装元素。
}

私人无效WriteCollectionElements(XmlWriter的作家)
{
如果(COUNT< 1)
的回报;
//序列化集合。
writer.WriteStartElement(CollectionItemsName);

无功序列化=新的XmlSerializer(typeof运算(T));
变种NS =新XmlSerializerNamespaces();
ns.Add(,); //禁用的xmlns:XSI和xmlns:XSD线。
的foreach(在此VAR​​项目)
{
serializer.Serialize(作家,项目,NS);
}

writer.WriteEndElement();
}

///<总结>
///编写所有自定义元素的的XmlReader
///< /总结>
///< PARAM NAME =作家>< /参数>
受保护的虚拟无效WriteCustomElements(XmlWriter的作家)
{
}

无效IXmlSerializable.ReadXml(XmlReader中读取)
{
如果(读者.IsEmptyElement)
{
reader.Read();
的回报;
}
reader.ReadStartElement(); //前进到包装元件的第一子元件。
,而(reader.NodeType!= XmlNodeType.EndElement)
{
如果(reader.NodeType!= XmlNodeType.Element)
//注释,空格
读卡器。读();
,否则如果(reader.IsEmptyElement)
reader.Read();
,否则如果(reader.Name == CollectionItemsName)
ReadCollectionElements(读卡器);
,否则如果(reader.Name == CollectionPropertiesName)
ReadCustomElements(读卡器);
,否则
//未知元素,跳过它。
reader.Skip();
}

//将过去的包装元素
reader.ReadEndElement年底();
}

无效ReadCustomElements(XmlReader中读取)
{
reader.ReadStartElement(); //前进到集合元件的第一子元件。
,而(reader.NodeType!= XmlNodeType.EndElement)
{
如果(reader.NodeType == XmlNodeType.Element)
{
使用(VAR subReader =读卡器.ReadSubtree())
{
,而(subReader.NodeType!= XmlNodeType.Element)//读取过去XmlNodeType.None
如果(!subReader.Read())
断点;
ReadCustomElement(subReader);
}
}
reader.Read();
}
//将过去的属性元件
reader.Read的端部();
}

无效ReadCollectionElements(XmlReader中读取)
{
无功序列化=新的XmlSerializer(typeof运算(T));
reader.ReadStartElement(); //前进到集合元件的第一子元件。
,而(reader.NodeType!= XmlNodeType.EndElement)
{
如果(reader.NodeType == XmlNodeType.Element)
{
使用(VAR subReader =读卡器.ReadSubtree())
{
,而(subReader.NodeType!= XmlNodeType.Element)//读取过去XmlNodeType.None
如果(!subReader.Read())
断点;
VAR项目=(T)serializer.Deserialize(subReader);
加入(项目);
}
}
reader.Read();
}
//将过去的集合元素
reader.Read年底();
}

///<总结>
///阅读从XmlReader中
///< ONE自定义元素; /总结>
///< PARAM NAME =读者>< /参数>
受保护的虚拟无效ReadCustomElement(XmlReader中读取)
{
}

的XmlSchema IXmlSerializable.GetSchema()
{
返回NULL;
}

#endregion
}

要使用这个类,你需要重写 ReadCustomElement(的XmlReader读卡器),其内容为一个自定义属性, WriteCustomElements (XmlWriter的作家),这将写入全部自定义属性。 (注不对称,这使得实现类层次结构更容易一些。)然后创建 Porudzbina 类,如下所示:

 公共类Porudzbina:XmlSerializableList< PorudzbenicaStavka> 
{
众长KomSifra {搞定;组; }
公众的Guid KomId {搞定;组; }

常量字符串KomSifraName =KomSifra;
常量字符串KomIdName =KomId;

保护覆盖无效WriteCustomElements(XmlWriter的作家)
{
writer.WriteElementString(KomSifraName,XmlConvert.ToString(KomSifra));
writer.WriteElementString(KomIdName,XmlConvert.ToString(KomId));
base.WriteCustomElements(作家);
}

保护覆盖无效ReadCustomElement(XmlReader中读取)
{
如果(reader.Name == KomSifraName)
{
KomSifra = reader.ReadElementContentAsLong();
}
,否则如果(reader.Name == KomIdName)
{
变种S = reader.ReadElementContentAsString();
KomId = XmlConvert.ToGuid(S);
}
,否则
{
base.ReadCustomElement(读卡器);
}
}
}

这将创建XML看起来这样的:




 < Porudzbina> 
<项目>
< PorudzbenicaStavka>
<! - 第一PorudzbenicaStavka的内容 - >
< / PorudzbenicaStavka>
<! - 其他PorudzbenicaStavka - >
< /项目>
<性状>
< KomSifra> 101 LT; / KomSifra>
< KomId> bb23a3b8-23d3-4edd-848B-d7621e6ed2c0< / KomId>
< /性状>
< / Porudzbina>



I have class that implement list of custom class. That class also has two properties. But when I serialize that class, XML contains only array of my custom classes but don't contains another two properties. Here is the class:

public class Porudzbina : List<PorudzbenicaStavka>, IEnumerable<SqlDataRecord>
{
    public long KomSifra { get; set; }
    public Guid KomId { get; set; }

    IEnumerator<SqlDataRecord> IEnumerable<SqlDataRecord>.GetEnumerator()
    {
        var sqlRow = new SqlDataRecord(
              new SqlMetaData("rb", SqlDbType.Int),
              new SqlMetaData("RobaSifra", SqlDbType.NVarChar, 50),
              new SqlMetaData("RobaNaziv", SqlDbType.NVarChar, 100)
             );
        foreach (PorudzbenicaStavka por in this)
        {
            sqlRow.SetInt32(0, por.rb);
            sqlRow.SetString(1, por.RobaSifra);
            sqlRow.SetString(2, por.RobaNaziv);
            yield return sqlRow;
        }
    }
}

and code that I use to serialize it:

    XmlSerializer serializer = new XmlSerializer(typeof(Porudzbina));
    using (TextWriter writer = new StreamWriter(@"C:\Xmle.xml"))
    {
        serializer.Serialize(writer, por);
    } 

and this is XML that I got:

    <?xml version="1.0" encoding="utf-8"?>
<ArrayOfPorudzbenicaStavka xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <PorudzbenicaStavka>
    <rb>1</rb>
    <RobaSifra>3702</RobaSifra>
    <RobaNaziv>Foullon mlecna cokolada 33% Ecuador 100g</RobaNaziv>    
  </PorudzbenicaStavka>
  <PorudzbenicaStavka>
    <rb>2</rb>
    <RobaSifra>1182</RobaSifra>
    <RobaNaziv>IL Capitano zelena maslina sa paprikom 720g</RobaNaziv>    
  </PorudzbenicaStavka>
  <PorudzbenicaStavka>
    <rb>3</rb>
    <RobaSifra>1120</RobaSifra>
    <RobaNaziv>Kaiser tuna steak sa papricicom u ulju 170g.</RobaNaziv>    
  </PorudzbenicaStavka>
</ArrayOfPorudzbenicaStavka>

I want my xml contains two properties together with a array of custom class, that I could deserialize it into its original state...

解决方案

The reason that your properties are not deserialized is explained in the documentation section Serializing a Class that Implements the ICollection Interface:

You can create your own collection classes by implementing the ICollection interface, and use the XmlSerializer to serialize instances of these classes. Note that when a class implements the ICollection interface, only the collection contained by the class is serialized. Any public properties or fields added to the class will not be serialized.

So, that's that.

You might consider changing your design so your classes do not have properties. For some reasons to make this change, see Why not inherit from List?.

If you nevertheless choose to have a collection with serializable properties, you're going to need to manually implement IXmlSerializable. This is burdensome, since you need to handle many "edge" cases including empty elements, unexpected elements, comments, and presence or absence of whitespace, all of which can throw off your ReadXml() method. For some background, see How to Implement IXmlSerializable Correctly.

First, create a base class for generic lists with serializable properties:

public class XmlSerializableList<T> : List<T>, IXmlSerializable where T : new()
{
    public XmlSerializableList() : base() { }

    public XmlSerializableList(IEnumerable<T> collection) : base(collection) { }

    public XmlSerializableList(int capacity) : base(capacity) { }

    #region IXmlSerializable Members

    const string CollectionItemsName = "Items";
    const string CollectionPropertiesName = "Properties";

    void IXmlSerializable.WriteXml(XmlWriter writer)
    {
        // Do not write the wrapper element.

        // Serialize the collection.
        WriteCollectionElements(writer);

        // Serialize custom properties.
        writer.WriteStartElement(CollectionPropertiesName);
        WriteCustomElements(writer);
        writer.WriteEndElement();

        // Do not end the wrapper element.
    }

    private void WriteCollectionElements(XmlWriter writer)
    {
        if (Count < 1)
            return;
        // Serialize the collection.
        writer.WriteStartElement(CollectionItemsName);

        var serializer = new XmlSerializer(typeof(T));
        var ns = new XmlSerializerNamespaces();
        ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
        foreach (var item in this)
        {
            serializer.Serialize(writer, item, ns);
        }

        writer.WriteEndElement();
    }

    /// <summary>
    /// Write ALL custom elements to the XmlReader
    /// </summary>
    /// <param name="writer"></param>
    protected virtual void WriteCustomElements(XmlWriter writer)
    {
    }

    void IXmlSerializable.ReadXml(XmlReader reader)
    {
        if (reader.IsEmptyElement)
        {
            reader.Read();
            return;
        }
        reader.ReadStartElement(); // Advance to the first sub element of the wrapper element.
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            if (reader.NodeType != XmlNodeType.Element)
                // Comment, whitespace
                reader.Read();
            else if (reader.IsEmptyElement)
                reader.Read();
            else if (reader.Name == CollectionItemsName)
                ReadCollectionElements(reader);
            else if (reader.Name == CollectionPropertiesName)
                ReadCustomElements(reader);
            else
                // Unknown element, skip it.
                reader.Skip();
        }

        // Move past the end of the wrapper element
        reader.ReadEndElement();
    }

    void ReadCustomElements(XmlReader reader)
    {
        reader.ReadStartElement(); // Advance to the first sub element of the collection element.
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                using (var subReader = reader.ReadSubtree())
                {
                    while (subReader.NodeType != XmlNodeType.Element) // Read past XmlNodeType.None
                        if (!subReader.Read())
                            break;
                    ReadCustomElement(subReader);
                }
            }
            reader.Read();
        }
        // Move past the end of the properties element
        reader.Read();
    }

    void ReadCollectionElements(XmlReader reader)
    {
        var serializer = new XmlSerializer(typeof(T));
        reader.ReadStartElement(); // Advance to the first sub element of the collection element.
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                using (var subReader = reader.ReadSubtree())
                {
                    while (subReader.NodeType != XmlNodeType.Element) // Read past XmlNodeType.None
                        if (!subReader.Read())
                            break;
                    var item = (T)serializer.Deserialize(subReader);
                    Add(item);
                }
            }
            reader.Read();
        }
        // Move past the end of the collection element
        reader.Read();
    }

    /// <summary>
    /// Read ONE custom element from the XmlReader
    /// </summary>
    /// <param name="reader"></param>
    protected virtual void ReadCustomElement(XmlReader reader)
    {
    }

    XmlSchema IXmlSerializable.GetSchema()
    {
        return null;
    }

    #endregion
}

To use this class, you will need to override ReadCustomElement(XmlReader reader), which reads a single custom property, and WriteCustomElements(XmlWriter writer), which writes all custom properties. (Note the asymmetry, it makes implementing class hierarchies a little easier.) Then create your Porudzbina class as follows:

public class Porudzbina : XmlSerializableList<PorudzbenicaStavka>
{
    public long KomSifra { get; set; }
    public Guid KomId { get; set; }

    const string KomSifraName = "KomSifra";
    const string KomIdName = "KomId";

    protected override void WriteCustomElements(XmlWriter writer)
    {
        writer.WriteElementString(KomSifraName, XmlConvert.ToString(KomSifra));
        writer.WriteElementString(KomIdName, XmlConvert.ToString(KomId));
        base.WriteCustomElements(writer);
    }

    protected override void ReadCustomElement(XmlReader reader)
    {
        if (reader.Name == KomSifraName)
        {
            KomSifra = reader.ReadElementContentAsLong();
        }
        else if (reader.Name == KomIdName)
        {
            var s = reader.ReadElementContentAsString();
            KomId = XmlConvert.ToGuid(s);
        }
        else
        {
            base.ReadCustomElement(reader);
        }
    }
}

This will create XML that looks like:

<Porudzbina>
    <Items>
        <PorudzbenicaStavka>
            <!-- contents of first PorudzbenicaStavka -->
        </PorudzbenicaStavka>
        <!-- Additional PorudzbenicaStavka -->
    </Items>
    <Properties>
        <KomSifra>101</KomSifra>
        <KomId>bb23a3b8-23d3-4edd-848b-d7621e6ed2c0</KomId>
    </Properties>
</Porudzbina>

这篇关于如何序列化的ICollection&LT; T&GT;还具有读/写属性来XML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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