2015-04-22 35 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

相關問題