为什么.NET中没有XML可序列化的字典?


73

我需要一个XML可序列化的字典。实际上,我现在有两个非常不同的程序,需要一个。看到.NET没有它,我感到非常惊讶。我在其他地方问了这个问题,并得到了讽刺的回答。我不明白为什么这是一个愚蠢的问题。

鉴于各种.NET功能对XML序列化的依赖性如何,有人可以启发我,为什么没有XML可序列化的字典。希望您也可以解释为什么有些人认为这个愚蠢的问题。我想我必须缺少一些基本知识,希望您能够填补空白。


5
这个问题是不正确的,因为它使因果关系错误。应该是“为什么XmlSerializer不能序列化字典”?因为在.NET中有很多方法可以进行XML序列化,而且大多数方法都可以对字典进行序列化(DataContractSerializerSoapFormatter...)。
帕维尔·米纳夫

我猜您还没有检查过“ XmlDictionaryWriter.CreateDictionaryWriter” ...或在.NET中序列化字典的其他100种方法(其中一些是内置的)。...为什么还需要字典?我总是发现强类型对象可以更好地工作,为什么不仅仅使用[DataContract]和IExtensibleDataObject实现一个类呢?
BrainSlugs83

您认为.NET中的哪些现代功能取决于XML序列化?配置文件不使用序列化,并且ASMX Web服务仅适用于旧版使用。(从评论中移至评论)
约翰·桑德斯

Answers:


14

关于XML序列化的问题不仅在于创建字节流。它还与创建此字节流将针对其进行验证的XML模式有关。XML Schema中没有好的方法来表示字典。您能做的最好的事情就是证明有一个唯一的钥匙。

您始终可以创建自己的包装器,例如“序列化词典的一种方法”


我的两个案例是Web服务和配置文件。因此,您是说.NET Framework专家受XML Schema规范的不足所限制?我在网上找到了东西,但是使用内置类的工作要比确定其他人是否做对的工作少得多。我将看一看您建议的那个。
serialhobbyist

ASMX Web服务现在被认为是传统技术。参见johnwsaundersiii.spaces.live.com/blog/…。有一个用于配置文件的完整API-它不使用XML序列化。还要别的吗?
约翰·桑德斯

顺便说一句,“限制”是设计决定。就像您说的那样,它用于Web服务-不仅用于序列化和反序列化-它是产生作为WSDL一部分的模式的原因。这是整体的一部分,并且必须一起工作。
约翰·桑德斯

我知道它们是传统,但这并不意味着我将有时间学习WCF。有人指出,软件不应该是镀金的,它应该可以完成工作。ASMX可以完成这项工作。微软开发.NET的步伐令人兴奋,奇妙,但与当前市场脱节:削减培训预算,削减预算,只做必须做的工作。当我们说“我们需要升级,因为微软将不再支持技术X”时,业务的非IT部门显得有些疑问。(我知道它不仅是MS,而且是经常使用的MS。)因此,我暂时仍停留在ASMX上。
serialhobbyist

您说过“鉴于各种.NET功能如何依赖XML序列化”,您无法理解为什么没有XML序列化。我说过.NET依赖XML Ser的功能很少。您提到了ASMX和Config。我说过ASMX是旧版,配置不使用XML Ser。“旧版”旨在说明为什么他们不急于增加字典支持。另请参阅johnwsaundersiii.spaces.live.com/blog/…
约翰·桑德斯

52

我知道以前已经回答过这个问题,但是由于我有一种非常简洁的方法(代码)来使用DataContractSerializer类(由WCF使用,但可以并且应该在任何地方使用)进行IDictionary序列化,所以我不能拒绝在这里提供它:

public static class SerializationExtensions
{
    public static string Serialize<T>(this T obj)
    {
        var serializer = new DataContractSerializer(obj.GetType());
        using (var writer = new StringWriter())
        using (var stm = new XmlTextWriter(writer))
        {
            serializer.WriteObject(stm, obj);
            return writer.ToString();
        }
    }
    public static T Deserialize<T>(this string serialized)
    {
        var serializer = new DataContractSerializer(typeof(T));
        using (var reader = new StringReader(serialized))
        using (var stm = new XmlTextReader(reader))
        {
            return (T)serializer.ReadObject(stm);
        }
    }
}

尽管我尚未对其进行测试,但它在.NET 4中可以完美地工作,并且在.NET 3.5中也可以工作。

更新:不支持.NET Compact Framework(甚至Windows Phone 7的NETCF 3.7也不适用)DataContractSerializer

我将流式传输到字符串是因为它对我来说更方便,尽管我可以向Stream中引入一个较低级别的序列化,然后使用它来序列化为字符串,但是我倾向于只在需要时进行泛化(就像过早的优化是邪恶的一样) ,这是过早的概括...)

用法很简单:

// dictionary to serialize to string
Dictionary<string, object> myDict = new Dictionary<string, object>();
// add items to the dictionary...
myDict.Add(...);
// serialization is straight-forward
string serialized = myDict.Serialize();
...
// deserialization is just as simple
Dictionary<string, object> myDictCopy = 
    serialized.Deserialize<Dictionary<string,object>>();

myDictCopy将是myDict的逐字记录副本。

您还将注意到,提供的泛型方法将能够序列化任何类型(据我所知),因为它不仅限于IDictionary接口,它实际上可以是任何泛型T。

希望它可以帮助某人!


5
很棒!对其他开发人员:System.Runtime.Serialization如果您还没有一个项目参考,则需要为其添加项目参考,但是该参考在.NET 4.0客户端配置文件中可用。
MCattle

它不适用于以Windows Phone 7.5(即Silverlight 3)为目标的Windows Phone 8 SDK。
2013年

@Adarsha根据DataContractSerializer文档,它支持以下平台:Windows 8,Windows Server 2012,Windows 7,Windows Vista SP2,Windows Server 2008(不支持服务器核心角色),Windows Server 2008 R2(SP1或更高版本支持服务器核心角色) ;不支持Itanium。出什么不起作用!谢谢阿达莎!
Loudenvier 2013年

我不知道为什么我没有看到过这一点,但-1使用new XmlTextWriternew XmlTextReader,而不是XmlReader.CreateXmlWriter.Create
约翰·桑德斯

1
@JohnSaunders当我已经知道我想要哪个XmlReader或Writer时,为什么要这样做。您是否看过XmlWriter / Reader.Create?调用它会有很大的损失,并且此方法应尽可能快地用于紧密循环序列化许多对象的过程中(我承认,如果此处的性能是问题,我将使用另一种序列化方法)。但是无论如何,推荐的方法是使用XmlWriter / Reader.Create,但是由于我是从一开始(版本1)开始对.NET进行编程的,所以我想我习惯于以“旧的”方式来做一些事情。
Loudenvier 2014年

13

他们在.NET 3.0中添加了一个。如果可以,请添加对System.Runtime.Serialization的引用,并查找System.Xml.XmlDictionary,System.Xml.XmlDictionaryReader和System.Xml.XmlDictionaryWriter。

我同意这不是一个特别可发现的地方。


4
这些类不是通用的可序列化词典。它们与WCF中序列化的实现有关。
约翰·桑德斯

我不明白这句话。为什么不是通用XML可序列化的字典?“ System.Xml.XmlDictionary”或“ System.Runtime.Serialization”的哪一部分表示非通用性?
罗宾·戴维斯

4

使用DataContractSerializer!请参阅下面的示例。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Xml;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.Value = 1;

            B b = new B();
            b.Value = "SomeValue";

            Dictionary<A, B> d = new Dictionary<A,B>();
            d.Add(a, b);
            DataContractSerializer dcs = new DataContractSerializer(typeof(Dictionary<A, B>));
            StringBuilder sb = new StringBuilder();
            using (XmlWriter xw = XmlWriter.Create(sb))
            {
                dcs.WriteObject(xw, d);
            }
            string xml = sb.ToString();
        }
    }

    public class A
    {
        public int Value
        {
            get;
            set;
        }
    }

    public class B
    {
        public string Value
        {
            get;
            set;
        }
    }
}

上面的代码生成以下xml:

<?xml version="1.0" encoding="utf-16"?>
<ArrayOfKeyValueOfABHtQdUIlS xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <KeyValueOfABHtQdUIlS>
        <Key xmlns:d3p1="http://schemas.datacontract.org/2004/07/ConsoleApplication1">
            <d3p1:Value>1</d3p1:Value>
        </Key>
        <Value xmlns:d3p1="http://schemas.datacontract.org/2004/07/ConsoleApplication1">
            <d3p1:Value>SomeValue</d3p1:Value>
        </Value>
    </KeyValueOfABHtQdUIlS>
</ArrayOfKeyValueOfABHtQdUIlS>

4

创建自己的一个:-),只读功能是红利,但是如果您需要除字符串之外的其他键,则该类需要进行一些修改...

namespace MyNameSpace
{
    [XmlRoot("SerializableDictionary")]
    public class SerializableDictionary : Dictionary<String, Object>, IXmlSerializable
    {
        internal Boolean _ReadOnly = false;
        public Boolean ReadOnly
        {
            get
            {
                return this._ReadOnly;
            }

            set
            {
                this.CheckReadOnly();
                this._ReadOnly = value;
            }
        }

        public new Object this[String key]
        {
            get
            {
                Object value;

                return this.TryGetValue(key, out value) ? value : null;
            }

            set
            {
                this.CheckReadOnly();

                if(value != null)
                {
                    base[key] = value;
                }
                else
                {
                    this.Remove(key);
                }               
            }
        }

        internal void CheckReadOnly()
        {
            if(this._ReadOnly)
            {
                throw new Exception("Collection is read only");
            }
        }

        public new void Clear()
        {
            this.CheckReadOnly();

            base.Clear();
        }

        public new void Add(String key, Object value)
        {
            this.CheckReadOnly();

            base.Add(key, value);
        }

        public new void Remove(String key)
        {
            this.CheckReadOnly();

            base.Remove(key);
        }

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            Boolean wasEmpty = reader.IsEmptyElement;

            reader.Read();

            if(wasEmpty)
            {
                return;
            }

            while(reader.NodeType != XmlNodeType.EndElement)
            {
                if(reader.Name == "Item")
                {
                    String key = reader.GetAttribute("Key");
                    Type type = Type.GetType(reader.GetAttribute("TypeName"));

                    reader.Read();
                    if(type != null)
                    {
                        this.Add(key, new XmlSerializer(type).Deserialize(reader));
                    }
                    else
                    {
                        reader.Skip();
                    }
                    reader.ReadEndElement();

                    reader.MoveToContent();
                }
                else
                {
                    reader.ReadToFollowing("Item");
                }

            reader.ReadEndElement();
        }

        public void WriteXml(XmlWriter writer)
        {
            foreach(KeyValuePair<String, Object> item in this)
            {
                writer.WriteStartElement("Item");
                writer.WriteAttributeString("Key", item.Key);
                writer.WriteAttributeString("TypeName", item.Value.GetType().AssemblyQualifiedName);

                new XmlSerializer(item.Value.GetType()).Serialize(writer, item.Value);

                writer.WriteEndElement();
            }
        }

    }
}

这段代码中有一个错误-如果xml中存在空格,则读数可能会进入无限循环。我已修复此错误,但可能还会更多。
路加福音

2

快速将IXmlSerializable添加到任何(现有)字典而不使用继承的通用帮助器:

using System.Xml;
using System.Xml.Serialization;
using System.Collections.Generic;

namespace GameSpace {

    public class XmlSerializerForDictionary {

        public struct Pair<TKey,TValue> {

            public TKey Key;
            public TValue Value;

            public Pair(KeyValuePair<TKey,TValue> pair) {
                Key = pair.Key;
                Value = pair.Value;
            }//method

        }//struct

        public static void WriteXml<TKey,TValue>(XmlWriter writer, IDictionary<TKey,TValue> dict) {

            var list = new List<Pair<TKey,TValue>>(dict.Count);

            foreach (var pair in dict) {
                list.Add(new Pair<TKey,TValue>(pair));
            }//foreach

            var serializer = new XmlSerializer(list.GetType());
            serializer.Serialize(writer, list);

        }//method

        public static void ReadXml<TKey, TValue>(XmlReader reader, IDictionary<TKey, TValue> dict) {

            reader.Read();

            var serializer = new XmlSerializer(typeof(List<Pair<TKey,TValue>>));
            var list = (List<Pair<TKey,TValue>>)serializer.Deserialize(reader);

            foreach (var pair in list) {
                dict.Add(pair.Key, pair.Value);
            }//foreach

            reader.Read();

        }//method

    }//class

}//namespace

和一个方便的可序列化的通用字典:

using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Collections.Generic;

namespace GameSpace {

    public class SerializableDictionary<TKey,TValue> : Dictionary<TKey,TValue>, IXmlSerializable {

        public virtual void WriteXml(XmlWriter writer) {
            XmlSerializerForDictionary.WriteXml(writer, this);
        }//method

        public virtual void ReadXml(XmlReader reader) {
            XmlSerializerForDictionary.ReadXml(reader, this);
        }//method

        public virtual XmlSchema GetSchema() {
            return null;
        }//method

    }//class

}//namespace

1

这是我的实现。

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;

namespace Rubik.Staging
{    
    [XmlSchemaProvider("GetInternalSchema")]
    public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
    {
        #region IXmlSerializable Members

        private const string ns = "http://www.rubik.com.tr/staging";

        public static XmlQualifiedName GetInternalSchema(XmlSchemaSet xs)
        {
            bool keyIsSimple = (typeof(TKey).IsPrimitive || typeof(TKey) == typeof(string));
            bool valueIsSimple = (typeof(TValue).IsPrimitive || typeof(TValue) == typeof(string));

            XmlSchemas schemas = new XmlSchemas();

            XmlReflectionImporter importer = new XmlReflectionImporter(ns);
            importer.IncludeType(typeof(TKey));            
            importer.IncludeType(typeof(TValue));            

            XmlTypeMapping keyMapping = importer.ImportTypeMapping(typeof(TKey));            
            XmlTypeMapping valueMapping = importer.ImportTypeMapping(typeof(TValue));          

            XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); 

            if(!keyIsSimple)
                exporter.ExportTypeMapping(keyMapping);
            if(!valueIsSimple)
                exporter.ExportTypeMapping(valueMapping);

            XmlSchema schema = (schemas.Count == 0 ? new XmlSchema() : schemas[0]);

            schema.TargetNamespace = ns;          
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            type.Name = "DictionaryOf" + keyMapping.XsdTypeName + "And" + valueMapping.XsdTypeName;
            XmlSchemaSequence sequence = new XmlSchemaSequence();
            XmlSchemaElement item = new XmlSchemaElement();
            item.Name = "Item";

            XmlSchemaComplexType itemType = new XmlSchemaComplexType();            
            XmlSchemaSequence itemSequence = new XmlSchemaSequence();

            XmlSchemaElement keyElement = new XmlSchemaElement();

            keyElement.Name = "Key";
            keyElement.MaxOccurs = 1;
            keyElement.MinOccurs = 1;

            XmlSchemaComplexType keyType = new XmlSchemaComplexType();
            XmlSchemaSequence keySequence = new XmlSchemaSequence();
            XmlSchemaElement keyValueElement = new XmlSchemaElement();
            keyValueElement.Name = keyMapping.ElementName;
            keyValueElement.SchemaTypeName = new XmlQualifiedName(keyMapping.XsdTypeName, keyMapping.XsdTypeNamespace);
            keyValueElement.MinOccurs = 1;
            keyValueElement.MaxOccurs = 1;
            keySequence.Items.Add(keyValueElement);
            keyType.Particle = keySequence;
            keyElement.SchemaType = keyType;
            itemSequence.Items.Add(keyElement);


            XmlSchemaElement valueElement = new XmlSchemaElement();

            valueElement.Name = "Value";
            valueElement.MaxOccurs = 1;
            valueElement.MinOccurs = 1;

            XmlSchemaComplexType valueType = new XmlSchemaComplexType();
            XmlSchemaSequence valueSequence = new XmlSchemaSequence();
            XmlSchemaElement valueValueElement = new XmlSchemaElement();
            valueValueElement.Name = valueMapping.ElementName;
            valueValueElement.SchemaTypeName = new XmlQualifiedName(valueMapping.XsdTypeName, valueMapping.XsdTypeNamespace);
            valueValueElement.MinOccurs = 1;
            valueValueElement.MaxOccurs = 1;
            valueSequence.Items.Add(valueValueElement);
            valueType.Particle = valueSequence;
            valueElement.SchemaType = valueType;
            itemSequence.Items.Add(valueElement);
            itemType.Particle = itemSequence;
            item.SchemaType = itemType;            
            sequence.Items.Add(item);
            type.Particle = sequence;
            schema.Items.Add(type);

            xs.XmlResolver = new XmlUrlResolver();
            xs.Add(schema);

            return new XmlQualifiedName(type.Name, ns);
        }





        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];
                valueSerializer.Serialize(writer, value);
                writer.WriteEndElement();

                writer.WriteEndElement();
            }
        }

        #endregion

        #region IXmlSerializable Members

        public XmlSchema GetSchema()
        {
            return null;
        }

        #endregion
    }

}

4
您应该评论使用更详细的解决方案而不是该线程其他地方提出的解决方案的优点。现在尚不清楚为什么要在更简单的实现中使用它。
Renaud Bompuis 2011年

1

我知道这已经死了,但这是我的贡献。我从@Loudenvier和@Jack的解决方案中获取了很多好处,并编写了自己的serialisable(对不起,我是英国人)字典类。

public class SerialisableDictionary<T1, T2> : Dictionary<T1, T2>, IXmlSerializable
{
    private static DataContractSerializer serializer =
        new DataContractSerializer(typeof(Dictionary<T1, T2>));

    public void WriteXml(XmlWriter writer)
    {
        serializer.WriteObject(writer, this);
    }

    public void ReadXml(XmlReader reader)
    {
        Dictionary<T1, T2> deserialised =
            (Dictionary<T1, T2>)serializer.ReadObject(reader);

        foreach(KeyValuePair<T1, T2> kvp in deserialised)
        {
            Add(kvp.Key, kvp.Value);
        }
    }

    public XmlSchema GetSchema()
    {
        return null;
    }
}

我喜欢这种方法,因为您不必显式地序列化或反序列化任何内容,只需通过XmlSerializer注入整个类层次结构即可。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.