2016-03-04 61 views
1

這裏是XML當key是Xml節點名稱時,如何將xml反序列化爲字典?

<?xml version="1.0"?> 
<TransactionLog> 
    <RuleViolations> 
    <error> 
     <message>error1</message> 
     <keys> 
     <key1>val1</key1> 
     <key2>val2</key2> 
     <key3>val3</key3> 
     <key4>val4</key4> 
     </keys> 
    </error> 
    <error> 
     <message>error1</message> 
     <keys> 
     <key1>val5</key1> 
     <key2>val6</key2> 
     </keys> 
    </error> 
    <error> 
     <message>error3</message> 
     <keys> 
     <key2>val7</key2> 
     <key3>val8</key3> 
     <key4>val9</key4> 
     </keys> 
    </error> 
    </RuleViolations> 
</TransactionLog> 

我現在擁有的一切:

[XmlRoot("TransactionLog")] 
public class TransactionLogModel 
{ 
    [XmlArray("RuleViolations")] 
    [XmlArrayItem("error")] 
    public List<KeyValuePair<string,string>> RuleViolations { get; set; } 
} 

但如何才能序列化<keys>部分?


最近SO後我能找到的是在這裏:Deserialize XML into Dictionary

但我不使用XDocument

var x = new XmlSerializer(typeof(TransactionLogModel)); 
var model = (TransactionLogModel)x.Deserialize(new StringReader(log));  

我們該如何反序列化這個xml在XmlSerializer

+0

瓦在應該是反序列化類的內容?我不明白你想讓鑰匙在你的模型中出現在哪裏。 – TToni

+0

的KEY1,KEY2 ...是動態的?如果是這樣,你應該看看這個 - http://stackoverflow.com/questions/13353387/generic-xml-deserialization-into-undefined-objects –

回答

1

首先,您的數據模型與您的XML不匹配 - 在TransactionLogkeys之間有幾個中間類缺失。相反,它應該是這個樣子:

[XmlRoot("TransactionLog")] 
public class TransactionLogModel 
{ 
    [XmlElement("RuleViolations")] 
    public List<RuleViolation> RuleViolations { get; set; } 
} 

public class RuleViolation 
{ 
    public RuleViolation() { this.Errors = new List<Error>(); } 

    [XmlElement("error")] 
    public List<Error> Errors { get; set; } 
} 

public class Error 
{ 
    [XmlElement("message")] 
    public string Message { get; set; } 

    // To be done. 
    public List<KeyValuePair<string, string>> Keys { get; set; } 
} 

接下來,使用這些按鍵的名稱作爲元素名稱序列化List<KeyValuePair<string, string>> Keys,標準的解決方案是implement IXmlSerializable在適當的類型。這有點麻煩,但不是很糟糕,因爲你的pair值是基本類型(字符串),而不是複雜類型,需要嵌套序列化。

例如,你可以使用XmlKeyTextValueListWrapperSerialize Dictionary member to XML elements and data

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 
} 

然後使用它像:

public class Error 
{ 
    [XmlElement("message")] 
    public string Message { get; set; } 

    List<KeyValuePair<string, string>> keys; 

    [XmlIgnore] 
    public List<KeyValuePair<string, string>> Keys 
    { 
     get 
     { 
      // Ensure keys is never null. 
      return (keys = keys ?? new List<KeyValuePair<string, string>>()); 
     } 
     set 
     { 
      keys = value; 
     } 
    } 

    [XmlElement("keys")] 
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)] 
    public XmlKeyTextValueListWrapper<string> XmlKeys 
    { 
     get 
     { 
      return new XmlKeyTextValueListWrapper<string>(() => this.Keys); 
     } 
     set 
     { 
      value.CopyTo(Keys); 
     } 
    } 
} 

順便說一下,相同的解決方案將與public Dictionary<string, string> Keys性質的工作,只要確定字典是預分配:

public class Error 
{ 
    [XmlElement("message")] 
    public string Message { get; set; } 

    Dictionary<string, string> keys; 

    [XmlIgnore] 
    public Dictionary<string, string> Keys 
    { 
     get 
     { 
      // Ensure keys is never null. 
      return (keys = keys ?? new Dictionary<string, string>()); 
     } 
     set 
     { 
      keys = value; 
     } 
    } 

    [XmlElement("keys")] 
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)] 
    public XmlKeyTextValueListWrapper<string> XmlKeys 
    { 
     get 
     { 
      return new XmlKeyTextValueListWrapper<string>(() => this.Keys); 
     } 
     set 
     { 
      value.CopyTo(Keys); 
     } 
    } 
}