将Dictionary 成员序列化为XML元素和数据

我有一个可以序列化为XML的类“产品”。 我使用标准的System.Xml.Serialization.XmlSerializer来序列化,并使用XmlWriter“writer”对象将序列化结果写入StreamWriter对象。 序列化程序对象现在一次性序列化整个类:

XmlSerializer serializer = new XmlSerializer(typeof(products)); serializer.Serialize(writer, products); 

该类有一个名为’规范’的Dictionary 成员。 它是动态构建的,所以我事先不知道密钥。 以下是字典可能包含的数据示例(键:值):

  • 颜色:蓝色
  • 长度:110mm
  • 宽度:55mm

我希望能够将该属性序列化为:

 ...  blue 110mm 55mm  ... 

我知道这是糟糕的XML设计,但它必须符合第三方规范。

是否可以使用标准属性? 如果没有,我怎么能这样序列化字典?

如果您需要更多代码段,请告诉我们。

编辑 :由于需求的一些变化,我放开了Dictionary 。 相反,我创建了一个类“规范”:

 public class Specification { public string Name; public string Value; public bool IsOther; public Specification() : this(null, null, false) { } public Specification(string name, string value) : this(name, value, false) { } public Specification(string name, string value, bool isOther) { Name = name; Value = value; IsOther = isOther; } } 

为避免在产品类中使用List“List”重复元素“spec”,我使用了一个实现IXmlSerializable接口的复数类“Specifications”:

 public class Specifications: IXmlSerializable { public List Specs = new List(); public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { //I don't need deserialization, but it would be simple enough now. throw new System.NotImplementedException(); } public void WriteXml(XmlWriter writer) { //write all "standarad", named specs //this writes the blue-like elements Specs.Where(s => !s.IsOther).ToList().ForEach(s => writer.WriteElementString(s.Name, s.Value)); //write other specs //this writes {name|value[;]}* string otherSpecs = string.Join(";", Specs.Where(s => s.IsOther).Select(s => string.Concat(s.Name, "|", s.Value))); if (otherSpecs.Length > 0) writer.WriteElementString("other_specs", otherSpecs); } } 

“规格”类适用于:

 public class Product { public Product() { Specifications = new Specifications(); } [XmlElement("specs")] public Specifications Specifications; //this "feature" will not include  when there are none [XmlIgnore] public bool SpecificationsSpecified { get { return Specifications.Specs.Any(); } } //... } 

感谢您提供IXmlSerializable和XmlWriter的示例。 我不知道XmlWriter的界面和用法 – 它对我来说是一个有价值的灵感!

*这是我的第一个问题。 什么是最合适的关闭方式? 我没有提供这个作为我自己的答案,因为它不是我最初的问题(关于词典)的真实答案。

假设您的字典值都是可以转换为字符串的简单类型,您可以创建自己的IXmlSerializable字典包装来存储和检索键和值:

 public class XmlKeyTextValueListWrapper : CollectionWrapper>, IXmlSerializable { public XmlKeyTextValueListWrapper() : base(new List>()) { } // For deserialization. public XmlKeyTextValueListWrapper(ICollection> baseCollection) : base(baseCollection) { } public XmlKeyTextValueListWrapper(Func>> getCollection) : base(getCollection) {} #region IXmlSerializable Members public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { var converter = TypeDescriptor.GetConverter(typeof(TValue)); XmlKeyValueListHelper.ReadXml(reader, this, converter); } public void WriteXml(XmlWriter writer) { var converter = TypeDescriptor.GetConverter(typeof(TValue)); XmlKeyValueListHelper.WriteXml(writer, this, converter); } #endregion } public static class XmlKeyValueListHelper { public static void WriteXml(XmlWriter writer, ICollection> collection, TypeConverter typeConverter) { foreach (var pair in collection) { writer.WriteStartElement(XmlConvert.EncodeName(pair.Key)); writer.WriteValue(typeConverter.ConvertToInvariantString(pair.Value)); writer.WriteEndElement(); } } public static void ReadXml(XmlReader reader, ICollection> collection, TypeConverter typeConverter) { if (reader.IsEmptyElement) { reader.Read(); return; } reader.ReadStartElement(); // Advance to the first sub element of the list element. while (reader.NodeType == XmlNodeType.Element) { var key = XmlConvert.DecodeName(reader.Name); string value; if (reader.IsEmptyElement) { value = string.Empty; // Move past the end of item element reader.Read(); } else { // Read content and move past the end of item element value = reader.ReadElementContentAsString(); } collection.Add(new KeyValuePair(key, (T)typeConverter.ConvertFromInvariantString(value))); } // Move past the end of the list element reader.ReadEndElement(); } public static void CopyTo(this XmlKeyTextValueListWrapper collection, ICollection> dictionary) { if (dictionary == null) throw new ArgumentNullException("dictionary"); if (collection == null) dictionary.Clear(); else { if (collection.IsWrapperFor(dictionary)) // For efficiency return; var pairs = collection.ToList(); dictionary.Clear(); foreach (var item in pairs) dictionary.Add(item); } } } public class CollectionWrapper : ICollection { readonly Func> getCollection; public CollectionWrapper(ICollection baseCollection) { if (baseCollection == null) throw new ArgumentNullException(); this.getCollection = () => baseCollection; } public CollectionWrapper(Func> getCollection) { if (getCollection == null) throw new ArgumentNullException(); this.getCollection = getCollection; } public bool IsWrapperFor(ICollection other) { if (other == Collection) return true; var otherWrapper = other as CollectionWrapper; return otherWrapper != null && otherWrapper.IsWrapperFor(Collection); } ICollection Collection { get { return getCollection(); } } #region ICollection Members public void Add(T item) { Collection.Add(item); } public void Clear() { Collection.Clear(); } public bool Contains(T item) { return Collection.Contains(item); } public void CopyTo(T[] array, int arrayIndex) { Collection.CopyTo(array, arrayIndex); } public int Count { get { return Collection.Count; } } public bool IsReadOnly { get { return Collection.IsReadOnly; } } public bool Remove(T item) { return Collection.Remove(item); } #endregion #region IEnumerable Members public IEnumerator GetEnumerator() { return Collection.GetEnumerator(); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } 

然后像这样使用它:

 [XmlRoot("products")] public class Products { public Products() { Specifications = new Dictionary(); } [XmlIgnore] [JsonProperty("specifications")] // For testing purposes, I compare Json.NET serialization before and after XML serialization. You can remove this. public Dictionary Specifications { get; set; } [XmlElement("specifications")] [JsonIgnore] // For testing purposes, I compare Json.NET serialization before and after XML serialization. You can remove this. public XmlKeyTextValueListWrapper XmlSpecifications { get { return new XmlKeyTextValueListWrapper(() => this.Specifications); } set { value.CopyTo(Specifications = (Specifications ?? new Dictionary())); } } } 

您的字典值是简单类型(可直接从文本转换为文本)这一事实使得可以避免XmlSerializer嵌套创建,这更复杂。 请看这里的例子。

使字典NonSerialized

 [XmlRoot("specifications")] public class Specifications { [NonSerialized] Dictionary dict { get; set; } [XmlElement("color")] string color {get;set;} [XmlElement("length")] string length { get; set; } [XmlElement("width")] string width { get; set; } public Specifications() { dict = new Dictionary(); } }