我需要一个XML可序列化的字典。实际上,我现在有两个完全不同的程序需要一个。我很惊讶地看到.NET没有。我在其他地方问了这个问题并得到了讽刺的回应。我不明白为什么这是一个愚蠢的问题。
有人可以启发我,考虑到各种.NET功能在XML序列化方面的依赖性,为什么没有XML可序列化的字典。希望你也可以解释为什么有些人认为这是一个愚蠢的问题。我想我必须遗漏一些基本的东西,我希望你能填补空白。
答案 0 :(得分:52)
我知道之前已经回答了这个问题,但是因为我有一个非常简洁的方法(代码)用于使用DataContractSerializer类进行IDictionary序列化(由WCF使用,但可以并且应该在任何地方使用)我无法抗拒它这里:
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.
希望有人帮助那里的人!
答案 1 :(得分:14)
关于XML序列化的事情是它不仅仅是创建一个字节流。它还涉及创建这个字节流将验证的XML Schema。 XML Schema中没有很好的方法来表示字典。你能做的最好的事情就是表明有一把独特的钥匙。
您始终可以创建自己的包装器,例如One Way to Serialize Dictionaries。
答案 2 :(得分:13)
他们在.NET 3.0中添加了一个。如果可以,请添加对System.Runtime.Serialization的引用,并查找System.Xml.XmlDictionary,System.Xml.XmlDictionaryReader和System.Xml.XmlDictionaryWriter。
我同意它不是特别容易被发现的地方。
答案 3 :(得分:4)
创建一个你自己的:-),readonly功能是奖金,但如果你需要一个字符串以外的键,那么该类需要一些修改......
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();
}
}
}
}
答案 4 :(得分: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>
答案 5 :(得分:2)
一个通用助手,可以快速将IXmlSerializable添加到任何(现有)Dictionary而不使用继承:
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
答案 6 :(得分: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
}
}
答案 7 :(得分:1)
我知道现在已经死了,但这是我的贡献。我从@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抽取整个类层次结构就可以了。