如何从不使用 XElement 的自定义 XML 序列化/反序列化为 `Dictionary<int, string>`?
Posted
技术标签:
【中文标题】如何从不使用 XElement 的自定义 XML 序列化/反序列化为 `Dictionary<int, string>`?【英文标题】:How to serialize/deserialize to `Dictionary<int, string>` from custom XML not using XElement? 【发布时间】:2012-09-15 06:32:36 【问题描述】:有空的Dictionary<int, string>
如何用来自 XML 的键和值来填充它,比如
<items>
<item id='int_goes_here' value='string_goes_here'/>
</items>
并在不使用 XElement 的情况下将其序列化回 XML?
【问题讨论】:
解释为什么您不想要特定解决方案会很有帮助(即使 XElement 可能不是正确的方法)。 为什么不想使用 XElement?序列化/反序列化可以用 linq 在一行中完成。 【参考方案1】:借助一个临时的item
类
public class item
[XmlAttribute]
public int id;
[XmlAttribute]
public string value;
示例字典:
Dictionary<int, string> dict = new Dictionary<int, string>()
1,"one", 2,"two"
;
.
XmlSerializer serializer = new XmlSerializer(typeof(item[]),
new XmlRootAttribute() ElementName = "items" );
序列化
serializer.Serialize(stream,
dict.Select(kv=>new item()id = kv.Key,value=kv.Value).ToArray() );
反序列化
var orgDict = ((item[])serializer.Deserialize(stream))
.ToDictionary(i => i.id, i => i.value);
----------------------------------------------- -------------------------------------------
如果您改变主意,使用 XElement 可以做到这一点。
序列化
XElement xElem = new XElement(
"items",
dict.Select(x => new XElement("item",new XAttribute("id", x.Key),new XAttribute("value", x.Value)))
);
var xml = xElem.ToString(); //xElem.Save(...);
反序列化
XElement xElem2 = XElement.Parse(xml); //XElement.Load(...)
var newDict = xElem2.Descendants("item")
.ToDictionary(x => (int)x.Attribute("id"), x => (string)x.Attribute("value"));
【讨论】:
我在循环中使用你的代码。而且我有内存泄漏。我添加了 XmlSerializer.dispose()。但是没有变化……正常吗? XmlSerializer 的一些构造函数泄漏内存(临时生成的程序集) - 你应该缓存它们参见support.microsoft.com/en-us/help/886385/… @L.B 是否可以扩展它以序列化 DictionaryPaul Welter 的ASP.NET blog 有一个可序列化的字典。但它不使用属性。我会在代码下面解释原因。
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
[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];
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
#endregion
首先,此代码有一个问题。假设您从另一个来源阅读了一本字典:
<dictionary>
<item>
<key>
<string>key1</string>
</key>
<value>
<string>value1</string>
</value>
</item>
<item>
<key>
<string>key1</string>
</key>
<value>
<string>value2</string>
</value>
</item>
</dictionary>
这将在反序列化时引发异常,因为字典只能有一个键。
您必须在序列化字典中使用 XElement 的原因是字典未定义为 Dictionary<String,String>
,字典为 Dictionary<TKey,TValue>
。
要看到问题,问问你自己:假设我们有一个TValue
,它序列化为使用元素的东西,它把自己描述为 XML(假设一个字典字典 Dictionary<int,Dictionary<int,string>>
(这种模式并不罕见) ,它是一个查找表)),您的 Attribute only 版本将如何完全表示一个属性内的字典?
【讨论】:
您通过调用“ReadXml”方法进行反序列化。 您想要WriteStartElement("item")
和ReadStartElement("item")
等等在 foreach
循环中,对吧?
能否请您举例说明如何使用这个类?【参考方案3】:
字典在 C# 中默认是不可序列化的,我不知道为什么,但这似乎是一种设计选择。
现在,我建议使用Json.NET 将其转换为 JSON 并从那里转换为字典(反之亦然)。除非您真的需要 XML,否则我建议您完全使用 JSON。
【讨论】:
据我了解,他想将 XML 反序列化为字典。如果 Dictionary 是可序列化的,您可以使用 XmlSerializer 等内置类来完成任务,但正如我所说,它不是。【参考方案4】:基于 L.B. 的回答。
用法:
var serializer = new DictionarySerializer<string, string>();
serializer.Serialize("dictionary.xml", _dictionary);
_dictionary = _titleDictSerializer.Deserialize("dictionary.xml");
通用类:
public class DictionarySerializer<TKey, TValue>
[XmlType(TypeName = "Item")]
public class Item
[XmlAttribute("key")]
public TKey Key;
[XmlAttribute("value")]
public TValue Value;
private XmlSerializer _serializer = new XmlSerializer(typeof(Item[]), new XmlRootAttribute("Dictionary"));
public Dictionary<TKey, TValue> Deserialize(string filename)
using (FileStream stream = new FileStream(filename, FileMode.Open))
using (XmlReader reader = XmlReader.Create(stream))
return ((Item[])_serializer.Deserialize(reader)).ToDictionary(p => p.Key, p => p.Value);
public void Serialize(string filename, Dictionary<TKey, TValue> dictionary)
using (var writer = new StreamWriter(filename))
_serializer.Serialize(writer, dictionary.Select(p => new Item() Key = p.Key, Value = p.Value ).ToArray());
【讨论】:
【参考方案5】:我有一个结构 KeyValuePairSerializable
:
[Serializable]
public struct KeyValuePairSerializable<K, V>
public KeyValuePairSerializable(KeyValuePair<K, V> pair)
Key = pair.Key;
Value = pair.Value;
[XmlAttribute]
public K Key get; set;
[XmlText]
public V Value get; set;
public override string ToString()
return "[" + StringHelper.ToString(Key, "") + ", " + StringHelper.ToString(Value, "") + "]";
那么,Dictionary
属性的 XML 序列化是:
[XmlIgnore]
public Dictionary<string, string> Parameters get; set;
[XmlArray("Parameters")]
[XmlArrayItem("Pair")]
[DebuggerBrowsable(DebuggerBrowsableState.Never)] // not necessary
public KeyValuePairSerializable<string, string>[] ParametersXml
get
return Parameters?.Select(p => new KeyValuePairSerializable<string, string>(p)).ToArray();
set
Parameters = value?.ToDictionary(i => i.Key, i => i.Value);
只有属性必须是数组,而不是列表。
【讨论】:
【参考方案6】:您可以使用ExtendedXmlSerializer。 如果你有课:
public class TestClass
public Dictionary<int, string> Dictionary get; set;
并创建此类的实例:
var obj = new TestClass
Dictionary = new Dictionary<int, string>
1, "First",
2, "Second",
3, "Other",
;
您可以使用 ExtendedXmlSerializer 序列化此对象:
var serializer = new ConfigurationContainer()
.UseOptimizedNamespaces() //If you want to have all namespaces in root element
.Create();
var xml = serializer.Serialize(
new XmlWriterSettings Indent = true , //If you want to formated xml
obj);
输出 xml 将如下所示:
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:sys="https://extendedxmlserializer.github.io/system" xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples;assembly=ExtendedXmlSerializer.Samples">
<Dictionary>
<sys:Item>
<Key>1</Key>
<Value>First</Value>
</sys:Item>
<sys:Item>
<Key>2</Key>
<Value>Second</Value>
</sys:Item>
<sys:Item>
<Key>3</Key>
<Value>Other</Value>
</sys:Item>
</Dictionary>
</TestClass>
您可以从nuget 安装 ExtendedXmlSerializer 或运行以下命令:
Install-Package ExtendedXmlSerializer
【讨论】:
【参考方案7】:编写一个类 A,它包含一个类 B 的数组。类 B 应该有一个 id 属性和一个 value 属性。将 xml 反序列化为 A 类。将 A 中的数组转换为所需的字典。
要序列化字典,将其转换为 A 类的实例,然后序列化...
【讨论】:
【参考方案8】:KeyedCollection 像字典一样工作并且是可序列化的。
首先创建一个包含键和值的类:
/// <summary>
/// simple class
/// </summary>
/// <remarks></remarks>
[Serializable()]
public class cCulture
/// <summary>
/// culture
/// </summary>
public string culture;
/// <summary>
/// word list
/// </summary>
public List<string> list;
/// <summary>
/// status
/// </summary>
public string status;
然后创建一个 KeyedCollection 类型的类,并将你的类的一个属性定义为键。
/// <summary>
/// keyed collection.
/// </summary>
/// <remarks></remarks>
[Serializable()]
public class cCultures : System.Collections.ObjectModel.KeyedCollection<string, cCulture>
protected override string GetKeyForItem(cCulture item)
return item.culture;
对序列化这种类型的数据很有用。
【讨论】:
【参考方案9】:我将可序列化的类用于不同模块之间的 WCF 通信。 下面是一个可序列化类的示例,它也用作 DataContract。 我的方法是使用 LINQ 的强大功能将 Dictionary 转换为 KeyValuePair 的开箱即用的可序列化 List:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Xml.Serialization;
namespace MyFirm.Common.Data
[DataContract]
[Serializable]
public class SerializableClassX
// since the Dictionary<> class is not serializable,
// we convert it to the List<KeyValuePair<>>
[XmlIgnore]
public Dictionary<string, int> DictionaryX
get
return SerializableList == null ?
null :
SerializableList.ToDictionary(item => item.Key, item => item.Value);
set
SerializableList = value == null ?
null :
value.ToList();
[DataMember]
[XmlArray("SerializableList")]
[XmlArrayItem("Pair")]
public List<KeyValuePair<string, int>> SerializableList get; set;
用法很简单 - 我为我的数据对象的字典字段 - DictionaryX 分配了一个字典。通过将分配的字典转换为 KeyValuePair 的可序列化 List,在 SerializableClassX 内部支持序列化:
// create my data object
SerializableClassX SerializableObj = new SerializableClassX(param);
// this will call the DictionaryX.set and convert the '
// new Dictionary into SerializableList
SerializableObj.DictionaryX = new Dictionary<string, int>
"Key1", 1,
"Key2", 2,
;
【讨论】:
【参考方案10】:Sharpeserializer(开源)有一个简单的方法:
http://www.sharpserializer.com/
可以直接序列化/反序列化字典。
不需要用任何属性标记你的对象,也不需要在 Serialize 方法中给出对象类型(参见here)。
通过 nuget 安装:Install-package sharpserializer
那么就很简单了:
Hello World(来自官网):
// create fake obj
var obj = createFakeObject();
// create instance of sharpSerializer
// with standard constructor it serializes to xml
var serializer = new SharpSerializer();
// serialize
serializer.Serialize(obj, "test.xml");
// deserialize
var obj2 = serializer.Deserialize("test.xml");
【讨论】:
以上是关于如何从不使用 XElement 的自定义 XML 序列化/反序列化为 `Dictionary<int, string>`?的主要内容,如果未能解决你的问题,请参考以下文章
使用 XElement 保存 XML 文件时,文件中的对齐方式也会发生变化,如何避免?
如何使用 XElement.Parse 解析 XML 而无需查看/将实体更改为字符?
如何将 Dictionary<XElement,XElement> 转换为 xml 文件?