为什么 .NET 中没有 XML 可序列化字典?
我需要一个 XML 可序列化字典。 事实上,我现在有两个完全不同的程序需要一个。 我很惊讶地发现 .NET 没有这个。
有人可以启发我,考虑到各种 .NET 功能对 XML 序列化的依赖程度,为什么没有 XML 可序列化字典?
I need an XML-serializable dictionary. Actually, I now have two quite different programs that need one. I was rather surprised to see that .NET doesn't have one.
Can someone enlighten me, given how dependent various .NET features are on XML serialization, why there isn't an XML-serializable dictionary?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(8)
使用 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>
创建您自己的一个:-),只读功能是额外的好处,但如果您需要字符串以外的键,那么该类需要进行一些修改...
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();
}
}
}
}
一个通用帮助器,可以在不使用继承的情况下快速将 IXmlSerialized 添加到任何(现有)字典:
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
这是我的实现。
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
}
}
我知道现在这已经被搞死了,但这是我的贡献。 我从@Loudenvier 和@Jack 的解决方案中汲取了精华,并编写了自己的可序列化(抱歉,我是英国人)字典类。
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 泵送整个类层次结构即可。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
我知道这个问题之前已经被回答过,但是由于我有一个非常简洁的方法(代码)来使用 DataContractSerializer 类(由 WCF 使用,但可以而且应该在任何地方使用)进行 IDictionary 序列化,我忍不住在这里贡献它
:在 .NET 4 中完美运行,也应该在 .NET 3.5 中运行,尽管我还没有测试它。
更新:它不能在.NET Compact Framework 中工作(甚至不能在 Windows Phone 7 的 NETCF 3.7 中工作),因为不支持
DataContractSerializer
!我进行了流式传输到字符串,因为它对我来说更方便,尽管我可以向 Stream 引入较低级别的序列化,然后使用它来序列化为字符串,但我倾向于仅在需要时进行概括(就像过早优化是邪恶的一样) ,所以这是不成熟的概括...)
用法非常简单:
myDictCopy 将是 myDict 的逐字副本。
您还会注意到,提供的泛型方法将能够序列化任何类型(据我所知),因为它不限于 IDictionary 接口,它实际上可以是任何泛型类型 T。
希望它对那里的人有帮助!
I know this has been answered before, but since I have a very concise way (code) for doing IDictionary serialization with the DataContractSerializer class (used by WCF, but could and should be used anywhere) I couldn't resist contributing it here:
This works perfectly in .NET 4 and should also work in .NET 3.5, although I didn't test it yet.
UPDATE: It doesn't work in .NET Compact Framework (not even NETCF 3.7 for Windows Phone 7) as the
DataContractSerializer
is not supported!I did the streaming to string because it was more convenient to me, although I could have introduced a lower-level serialization to Stream and then used it to serialize to strings, but I tend to generalize only when needed (just like premature optimization is evil, so it is premature generalization...)
Usage is very simple:
myDictCopy will be a verbatim copy of myDict.
You'll also notice that the generic methods provided will be able to serialize any type (to the best of my knowledge) since it is not limited to IDictionary interfaces, it can be really any generic type T.
Hope it helps someone out there!