字典XML序列化

我有一个类,我想序列化的字典属性。 我已经读过序列化字典不是直接可能的,所以我序列化/反序列化列表然后转换为字典。 它有效,但我想知道是否有一个正确的方法来做到这一点。

[Serializable] public class Album { private List photos = new List(); [XmlArray] public List Photos { get { return photos; } set { photos = value; } } private Dictionary dicoPhotos = new Dictionary(); [XmlIgnore] public Dictionary DicoPhotos { get { return dicoPhotos; } set { dicoPhotos = value; } } public void fillPhotosDictionnary() { this.dicoPhotos = this.photos.ToDictionary(p => p.Nom, p => p); } } 

我试图在Photo的setter中填写字典,但它不起作用,我无法弄清楚为什么。

我建议利用DataContractSerializer及其相关属性( DataContractDataMember )。 它在.NET 3.0+中可用。

它与XmlSerializer非常相似,但我发现它在一般序列化方面要好得多。 此外,它支持开箱即用的Dictionary序列化。

在你的情况下,将一些属性切换到适当的DataContract属性应该是一个简单的事情,然后使用DataContractSerializer而不是XmlSerializer。

最后,输出将基本相同(XML文档),并且您的类中所需的代码更清晰。

 [DataContract(Name = "Album", Namespace = "DataContracts")] public class Album { [DataMember(Name = "DicoPhotos")] private Dictionary dicoPhotos = new Dictionary(); public Dictionary DicoPhotos { get { return dicoPhotos; } set { dicoPhotos = value; } } } 

DataContractSerializer周围有一些技巧和陷阱:

  • 确保它知道您要序列化的类型列表。
  • 确保所有内容都具有适当的名称和名称空间(以保护自己免受属性名称和名称空间更改)。

如果您仅为非持久性目的进行序列化(即通过电汇),请考虑使用NetDataContractSerializer。 如果你坚持使用任何类型的永久构造(如磁盘/数据库),或者你以后可能会有一个严重的问题(由于它的序列化方式),请不要使用它。

尝试使用Protobuf-net。 然后代码将类似于以下内容:

  [Serializable] [ProtoContract] public class Album { private List photos = new List(); [ProtoMember(1)] public List Photos { get { return photos; } set { photos = value; } } private Dictionary dicoPhotos = new Dictionary(); [ProtoMember(2)] public Dictionary DicoPhotos { get { return dicoPhotos; } set { dicoPhotos = value; } } } 

和序列化方法:

 public void Serialize(Object obj, String FileFullPath) { byte[] serialized; using (var ms = new MemoryStream()) { Serializer.Serialize(ms, obj); serialized = ms.ToArray(); } File.WriteAllBytes(FileFullPath, serialized); } 

它不起作用,因为XmlSerializer一个接一个地存储列表中的项目,因此在反序列化期间不一定要调用setter。 你可以做几个选择:

  1. 构建一个实现IList的包装类,并将调用路由到底层字典; 要么
  2. 实现IXmlSerializable并自己完成整个类的(反)序列化; 要么
  3. 在反序列化之后调用“fix-up”方法(如你的fillPhotosDictionnary )。

就个人而言,我选择选项(1),因为Dictionary序列化是一个常见问题,你也可能最终需要在其他类中使用它。

我意识到这个问题有点陈旧但我并不喜欢我找到的其他解决方案,因为他们以不同于我想要的方式格式化xml。 所以我这样做了。

我根据字典键创建了一个自定义动态类型,创建了这个新类型的实例,然后序列化该对象。

  public void Serialize(string OutPath, Dictionary Input) { //Define stuff AssemblyName assName = new AssemblyName("CustomType"); AssemblyBuilder assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave); ModuleBuilder modBuilder = assBuilder.DefineDynamicModule(assName.Name); TypeBuilder typBuilder = modBuilder.DefineType("NewType", TypeAttributes.Public); //Add all the keys as fields foreach (string Key in Input.Keys) { typBuilder.DefineField(Key, Input[Key].GetType(), FieldAttributes.Public); } //Make the new type Type newType = typBuilder.CreateType(); //make instance object newInstance = Activator.CreateInstance(newType); //set Values foreach (string Key in Input.Keys) { newInstance.GetType().GetField(Key).SetValue(newInstance, Input[Key]); } //serialize XML XmlSerializer xs = new XmlSerializer(newType); TextWriter tw = new StreamWriter(OutPath); xs.Serialize(tw, newInstance); tw.Flush(); tw.Close(); } 

如果要包含具有其他类型的原始字段的字典,可以在参数中包含该对象以传递值,并将这样的内容添加到适当的位置:

  //This part adds the fields to the custom type, include near where the dictionary keys are being added as fields foreach (FieldInfo FI in InputObject.GetType().GetFields()) { if (FI.FieldType.IsPrimitive || FI.FieldType == typeof(string)) { FieldInfo fi = newInstance.GetType().GetField(FI.Name); fi.SetValue(newInstance, FI.GetValue(InputObject)); } } //This part adds the values to the object, include near where the values are being set foreach (FieldInfo FI in InputObject.GetType().GetFields()) { if (FI.FieldType.IsPrimitive || FI.FieldType == typeof(string)) { FieldBuilder fieBuilder = typBuilder.DefineField(FI.Name, FI.FieldType, FieldAttributes.Public); } } 

这段代码:

  //build dictionary Dictionary Input = new Dictionary(); Input.Add("Value1", "One"); Input.Add("Value2", "Two"); Input.Add("Value3", 3); Input.Add("Date", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); //Serialize! Serialize(FilePath, Input); 

像这样构建一个xml输出:

   -  One Two 3 2014-08-28 18:03:58  

然后你可以像这样读入/反序列化xml:

  public Dictionary Deserialize(string OutPath) { Dictionary Output = new Dictionary(); //create the xmlDocument XmlDocument xd = new XmlDocument(); xd.Load(XmlReader.Create(OutPath)); //Scan all the nodes in the main doc and add them to the dictionary //you can recursively check child nodes if your document requires. foreach (XmlNode node in xd.DocumentElement) { Output.Add(node.Name, node.InnerText); } return Output; }