2015-04-22 32 views
1

我有一个序列化为XML的类“产品”。我使用标准的System.Xml.Serialization.XmlSerializer来序列化和一个XmlWriter'writer'对象来将序列化结果写入StreamWriter对象。串行器对象现在连载一气呵成全班同学:序列化字典<string,string>成员到XML元素和数据

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

类有一个字典<字符串,字符串>成员称为“规格”。它是动态构建的,所以我事先不知道密钥。这里的字典可能包含的数据的一个例子(键:值):

  • 颜色:蓝色
  • 长度:110毫米
  • 宽度:55毫米

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

... 
<specifications> 
    <color>blue</color> 
    <length>110mm</length> 
    <width>55mm</width> 
</specifications> 
... 

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

是否有可以使用的标准属性?如果没有,我将如何能够像这样序列化字典?

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

编辑: 由于需要一些变化,我让字典<字符串,字符串>的去。相反,我创建了一个类“规范”:

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; 
    } 
} 

为了避免在产品类具有“规范”的列表重复元件“规格”,我使用了多个类的“规格”实现IXmlSerializable

public class Product 
{ 
    public Product() 
    { 
     Specifications = new Specifications(); 
    } 

    [XmlElement("specs")] 
    public Specifications Specifications; 

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

    //... 
} 

感谢你们提供了IXmlSerializable的和XmlWriter的例子:接口:作为

public class Specifications: IXmlSerializable 
{ 
    public List<Specification> Specs = new List<Specification>(); 

    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 <color>blue</color>-like elements 
     Specs.Where(s => !s.IsOther).ToList().ForEach(s => writer.WriteElementString(s.Name, s.Value)); 

     //write other specs 
     //this writes <other_specs>{name|value[;]}*</other_specs> 
     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); 
    } 
} 

类 “规范” 适用。我不知道XmlWriter的界面和用法 - 这对我来说是一个很有价值的灵感!

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

+0

我认为[此示例](http://huseyint.com/2007/12/xml-serializable-generic-dictionary-tipi/)可以帮助您:检查WriteXml方法并根据您的需要进行相应更改... – Marco

+0

你可能看看这里,因为字典本身在遗憾的不可序列化:http://stackoverflow.com/questions/12856456/c-sharp-serialize-dictionary-parameter-without-parent-node – HimBromBeere

+0

你事先知道将出现的密钥,还是需要序列化出现的任何密钥? – dbc

回答

1

假设你的字典值都是简单的类型,可以被转换成一个字符串,你可以创建自己的IXmlSerializable字典的包装来存储和检索按键和数值:

public class XmlKeyTextValueListWrapper<TValue> : CollectionWrapper<KeyValuePair<string, TValue>>, IXmlSerializable 
{ 
    public XmlKeyTextValueListWrapper() : base(new List<KeyValuePair<string, TValue>>()) { } // For deserialization. 

    public XmlKeyTextValueListWrapper(ICollection<KeyValuePair<string, TValue>> baseCollection) : base(baseCollection) { } 

    public XmlKeyTextValueListWrapper(Func<ICollection<KeyValuePair<string, TValue>>> 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<T>(XmlWriter writer, ICollection<KeyValuePair<string, T>> 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<T>(XmlReader reader, ICollection<KeyValuePair<string, T>> 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<string,T>(key, (T)typeConverter.ConvertFromInvariantString(value))); 
     } 
     // Move past the end of the list element 
     reader.ReadEndElement(); 
    } 

    public static void CopyTo<TValue>(this XmlKeyTextValueListWrapper<TValue> collection, ICollection<KeyValuePair<string, TValue>> 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<T> : ICollection<T> 
{ 
    readonly Func<ICollection<T>> getCollection; 

    public CollectionWrapper(ICollection<T> baseCollection) 
    { 
     if (baseCollection == null) 
      throw new ArgumentNullException(); 
     this.getCollection =() => baseCollection; 
    } 

    public CollectionWrapper(Func<ICollection<T>> getCollection) 
    { 
     if (getCollection == null) 
      throw new ArgumentNullException(); 
     this.getCollection = getCollection; 
    } 

    public bool IsWrapperFor(ICollection<T> other) 
    { 
     if (other == Collection) 
      return true; 
     var otherWrapper = other as CollectionWrapper<T>; 
     return otherWrapper != null && otherWrapper.IsWrapperFor(Collection); 
    } 

    ICollection<T> Collection { get { return getCollection(); } } 

    #region ICollection<T> 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<T> Members 

    public IEnumerator<T> GetEnumerator() 
    { 
     return Collection.GetEnumerator(); 
    } 

    #endregion 

    #region IEnumerable Members 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    #endregion 
} 

然后使用它li柯这样:

[XmlRoot("products")] 
public class Products 
{ 
    public Products() 
    { 
     Specifications = new Dictionary<string, string>(); 
    } 

    [XmlIgnore] 
    [JsonProperty("specifications")] // For testing purposes, I compare Json.NET serialization before and after XML serialization. You can remove this. 
    public Dictionary<string, string> 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<string> XmlSpecifications 
    { 
     get 
     { 
      return new XmlKeyTextValueListWrapper<string>(() => this.Specifications); 
     } 
     set 
     { 
      value.CopyTo(Specifications = (Specifications ?? new Dictionary<string, string>())); 
     } 
    } 
} 

你的字典中的值是简单类型(直接兑换,为文本)的事实使得能够避免XmlSerializer嵌套的创作,这是比较复杂的。一个例子见here

+0

这是在我对原始问题的评论链接中,最有价值的答案。看我的编辑我的最终解决方案。 – LDerckx

0

使字典非序列化

[XmlRoot("specifications")] 
 
    public class Specifications 
 
    { 
 
     [NonSerialized] 
 
     Dictionary<string, string> 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<string, string>(); 
 
     } 
 
    }

+0

事情是,我不知道哪个键会在里面。可能没有三,二十个。它是动态构建的,并且取决于每个产品。 – LDerckx

相关问题