当key是Xml节点名称时,如何将xml反序列化为字典?
问题描述:
这里是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
?
答
首先,您的数据模型与您的XML不匹配 - 在TransactionLog
和keys
之间有几个中间类缺失。相反,它应该是这个样子:
[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值是基本类型(字符串),而不是复杂类型,需要嵌套序列化。
例如,你可以使用XmlKeyTextValueListWrapper
从Serialize 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);
}
}
}
瓦在应该是反序列化类的内容?我不明白你想让钥匙在你的模型中出现在哪里。 – TToni
的KEY1,KEY2 ...是动态的?如果是这样,你应该看看这个 - http://stackoverflow.com/questions/13353387/generic-xml-deserialization-into-undefined-objects –