使用对象序列化程序序列化列表

本文关键字:序列化 程序 列表 对象 | 更新日期: 2023-09-27 18:01:07

我正在使用此博客条目中定义的SerializableDictionary来存储<string, object>数据,并将其传递给WCF服务。如果我使用值类型作为值,这很好,因为它们可以很容易地装箱,成为objects。但是,如果我使用之类的东西

new List<int>() { 5, 10 },我得到一个异常:

在此上下文中不能使用类型System.Collections.Generic.List`1[[System.Int32,mscorlib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089]。

根据这里的讨论,我应该使用value.GetType()来初始化XmlSerializer;然而,虽然这让我可以进行序列化,但我不知道如何以一般的方式反序列化回我的SerializableDictionary

我不确定是否有一种方法可以在允许<string, object>作为我的类型参数的同时彻底地改变这一点——我可以将值序列化为二进制而不是XML,并以这种方式传输它(相同的代码是序列化和反序列化的,所以我不关心互操作性(,但如果可能的话,我希望拥有XML。

编辑

完整代码示例:

XmlSerializer serializer = new XmlSerializer(typeof(SerializableDictionary<string, object>));
SerializableDictionary<string, object> dic = new SerializableDictionary<string, object>();
dic["test"] = new List<int>() { 5, 10 };
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb);
serializer.Serialize(writer, dic);
string ser = sb.ToString();

解决方案

感谢Nico Schertler给了我正确的答案。我将在这里发布我的最终代码,以备有人需要。这与第一个链接中的原始代码向后兼容,因此该代码序列化的任何内容都可以通过下面的代码进行反序列化。

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{
    #region " IXmlSerializable Members "
    #region " WriteXml "
    public void WriteXml(XmlWriter writer)
    {
        // Base types
        string baseKeyType = typeof(TKey).AssemblyQualifiedName;
        string baseValueType = typeof(TValue).AssemblyQualifiedName;
        writer.WriteAttributeString("keyType", baseKeyType);
        writer.WriteAttributeString("valueType", baseValueType);
        foreach (TKey key in this.Keys)
        {
            // Start
            writer.WriteStartElement("item");
            // Key
            Type keyType = key.GetType();
            XmlSerializer keySerializer = GetTypeSerializer(keyType.AssemblyQualifiedName);
            writer.WriteStartElement("key");
            if (keyType != typeof(TKey)) { writer.WriteAttributeString("type", keyType.AssemblyQualifiedName); }
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();
            // Value
            TValue value = this[key];
            Type valueType = value.GetType();
            XmlSerializer valueSerializer = GetTypeSerializer(valueType.AssemblyQualifiedName);
            writer.WriteStartElement("value");
            if (valueType != typeof(TValue)) { writer.WriteAttributeString("type", valueType.AssemblyQualifiedName); }
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();
            // End
            writer.WriteEndElement();
        }
    }
    #endregion
    #region " ReadXml "
    public void ReadXml(XmlReader reader)
    {
        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();
        if (wasEmpty)
        {
            return;
        }
        // Base types
        string baseKeyType = typeof(TKey).AssemblyQualifiedName;
        string baseValueType = typeof(TValue).AssemblyQualifiedName;
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            // Start
            reader.ReadStartElement("item");
            // Key
            XmlSerializer keySerializer = GetTypeSerializer(reader["type"] ?? baseKeyType);
            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();
            // Value
            XmlSerializer valueSerializer = GetTypeSerializer(reader["type"] ?? baseValueType);
            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();
            // Store
            this.Add(key, value);
            // End
            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }
    #endregion
    #region " GetSchema "
    public XmlSchema GetSchema()
    {
        return null;
    }
    #endregion
    #endregion
    #region " GetTypeSerializer "
    private static readonly Dictionary<string, XmlSerializer> _serializers = new Dictionary<string, XmlSerializer>();
    private static readonly object _deadbolt = new object();
    private XmlSerializer GetTypeSerializer(string type)
    {
        if (!_serializers.ContainsKey(type))
        {
            lock (_deadbolt)
            {
                if (!_serializers.ContainsKey(type))
                {
                    _serializers.Add(type, new XmlSerializer(Type.GetType(type)));
                }
            }
        }
        return _serializers[type];
    }
    #endregion
}

我只在类型与基本类型不同的情况下写出类型,以减少XML的长度,并且我保留了一个XmlSerializer的静态列表,以防止到处都是它们。我必须在一开始就写出提供的类型,以便能够防止在每个节点上写出类型。

使用对象序列化程序序列化列表

序列化的问题在于(反(序列化程序需要知道如何处理对象。object的序列化程序不知道如何序列化List<int>

对于序列化,您已经在问题中给出了答案。使用value.GetType()确定值的类型。此外,您必须保存类型本身。这可以通过类型(type.AssemblyQualifiedName(的字符串表示来容易地实现。

TValue value = this[key];
var type = value.GetType();
XmlSerializer valueSerializer = new XmlSerializer(type);
writer.WriteStartElement("type");
writer.WriteString(type.AssemblyQualifiedName); 
//you can use FullName if you don't need to address external libraries
writer.WriteEndElement();
writer.WriteStartElement("content");
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();

对于反序列化,您需要读取类型并反序列化值:

reader.ReadStartElement("value");
reader.ReadStartElement("type");
var typename = reader.ReadContentAsString();
reader.ReadEndElement();
var type = Type.GetType(typename);
XmlSerializer valueSerializer = new XmlSerializer(type);
reader.ReadStartElement("content");
TValue value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadEndElement();

在链接中引用的类中执行以下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Collections;
namespace sampleLogin
{
    [XmlRoot("dictionary")]
    public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
    {
        #region IXmlSerializable Members
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }
        public void ReadXml(System.Xml.XmlReader reader)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
            bool wasEmpty = reader.IsEmptyElement;
            reader.Read();
            if (wasEmpty)
            {
                return;
            }
            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
            {
                reader.ReadStartElement("item");
                reader.ReadStartElement("key");
                TKey key = (TKey)keySerializer.Deserialize(reader);
                reader.ReadEndElement();
                reader.ReadStartElement("value");
                TValue value = (TValue)valueSerializer.Deserialize(reader);
                reader.ReadEndElement();
                this.Add(key, value);
                reader.ReadEndElement();
                reader.MoveToContent();
            }
            reader.ReadEndElement();
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
            foreach (TKey key in this.Keys)
            {
                writer.WriteStartElement("item");
                writer.WriteStartElement("key");
                keySerializer.Serialize(writer, key);
                writer.WriteEndElement();
                writer.WriteStartElement("value");
                TValue value = this[key];
                var type = value.GetType();//new line added here
                valueSerializer = new XmlSerializer(type);//New line added here 
                valueSerializer.Serialize(writer, value);
                writer.WriteEndElement();
                writer.WriteEndElement();
            }
        }
        #endregion
    }