【问题标题】:Deserializing xml to object with dictionary使用字典将 xml 反序列化为对象
【发布时间】:2010-06-24 19:26:32
【问题描述】:

这是我的班级结构:

class DataItem
{
   public string Id { get; set; }
   public string Type { get; set; }

   private Dictionary<string, DataProperty> properties = new Dictionary<string, DataProperty>();

   public Dictionary<string, DataProperty> Properties
   {
       get { return properties; }
       set { properties = value; }
   }
}

public class DataProperty
{    
      public string Key { get; set; }
      public string Value { get; set; }
      public bool Required { get; set; }
}

这是我要使用的 XML(注意:如果需要,我可以更改 xml):

<Data>
  <DataItem>
    <id>ph15</id>
    <type>core</type>
    <properties>
      <DataProperty>
        <key>size</key>
        <value>50%</value>
        <required>false</required>
      </DataProperty>
      <DataProperty>
        <key>color</key>
        <value>red</value>
        <required>true</required>
      </DataProperty>
    </properties>
  </DataItem>
  <DataItem>
    <id>ph234</id>
    <type>core</type>
    <properties>
    </properties>
  </DataItem>
</Data>

最终应该将 XML 加载到另一个字典中:

private Dictionary<string, DataItem> Mydata;

【问题讨论】:

  • 我建议将 xml 映射到更直接的字段并使用属性进行 [de] 序列化。然后有逻辑从/到另一个数据结构[de]填充字典。 LINQ to xml 也可以成为你的朋友。

标签: c# xml


【解决方案1】:

很遗憾,通用字典不是 xml 可序列化的。

解决方法是增加一个单独的字段,专门用于支持将字典元素公开为键/值对列表的序列化。然后,您还必须使用 XmlIgnore 属性标记字典成员。

或者,您可以使用支持字典类型的 XmlSerializer(如 DataContractSerializer)以外的其他东西。

这是一个link to an article,它提供了一个很好的示例,说明如何修改您的类型以支持使用字典进行 XML 序列化。

如果您使用此代码,有一点很重要 - 序列化的项目可能会以任意顺序出现在输出中。这是使用字典(无序)作为数据存储模型的结果。

[XmlIgnore()]   
public Dictionary<string, DataProperty> Properties
{   
    set { properties = value; }   
    get { return properties ; }   
}


[XmlArray("Stuff")]   
[XmlArrayItem("StuffLine", Type=typeof(DictionaryEntry))]   
public DictionaryEntry[] PropertiesList
{   
    get  
    {   
        //Make an array of DictionaryEntries to return   
        DictionaryEntry[] ret=new DictionaryEntry[Properties.Count];   
        int i=0;   
        DictionaryEntry de;   
        //Iterate through Stuff to load items into the array.   
        foreach (KeyValuePair<string, DataProperty> props in Properties)   
        {   
            de = new DictionaryEntry();   
            de.Key = props.Key;   
            de.Value = props.Value;   
            ret[i]=de;   
            i++;   
        }   
        return ret;   
    }   
    set  
    {   
        Properties.Clear();   
        for (int i=0; i<value.Length; i++)   
        {   
            Properties.Add((string)value[i].Key, (DataProperty)value[i].Value);   
        }   
    }   
}  

【讨论】:

  • 我喜欢 DataContract,还有一个问题:因为 DataItem 类代表 Mydata 字典中的一个项目,我需要创建另一个类来包装它吗?
  • @ilann - 不,你不应该那样做。你应该能够直接序列化Mydata,只要你提供必要的元数据来定义成员将如何被序列化(即节点、属性、cdata等)。
【解决方案2】:

我知道这个问题之前已经回答过了,但是由于我有一个非常简洁的方法(代码)来使用 DataContractSerializer 类(由 WCF 使用,但可以而且应该在任何地方使用)进行 IDictionary 序列化,我忍不住贡献了​​它这里:

public static class SerializationExtensions
{
    public static string Serialize<T>(this T obj)
    {
        var serializer = new DataContractSerializer(obj.GetType());
        using (var writer = new StringWriter())
        using (var stm = new XmlTextWriter(writer))
        {
            serializer.WriteObject(stm, obj);
            return writer.ToString();
        }
    }
    public static T Deserialize<T>(this string serialized)
    {
        var serializer = new DataContractSerializer(typeof(T));
        using (var reader = new StringReader(serialized))
        using (var stm = new XmlTextReader(reader))
        {
            return (T)serializer.ReadObject(stm);
        }
    }
}

这在 .NET 4 中可以完美运行,并且在 .NET 3.5 中也应该可以运行,尽管我还没有对其进行测试。我将流式传输到字符串是因为它对我来说更方便,尽管我可以向 Stream 引入较低级别的序列化,然后用它来序列化到字符串,但我倾向于仅在需要时进行泛化(就像过早的优化是邪恶的一样,所以它是过早的泛化...)

用法很简单:

// dictionary to serialize to string
Dictionary<string, object> myDict = new Dictionary<string, object>();
// add items to the dictionary...
myDict.Add(...);
// serialization is straight-forward
string serialized = myDict.Serialize();
...
// deserialization is just as simple
Dictionary<string, object> myDictCopy = 
    serialized.Deserialize<Dictionary<string,object>>();

myDictCopy 将是 myDict 的逐字副本。

您还会注意到,所提供的泛型方法将能够序列化任何类型(据我所知),因为它不限于 IDictionary 接口,它实际上可以是任何泛型类型 T。

希望它可以帮助那里的人!

【讨论】:

    【解决方案3】:

    我建议您使用 DataContractSerializer。它确实支持字典。

    【讨论】:

      【解决方案4】:

      您不能只用另一种方法来创建一个包含 Value 和 Key 属性的简单对象。然后使用 List(Of thatObject) 它应该可以工作。但这取决于您是否只想传输值键数据,因为作为字典一部分的所有其他方法将不可用。

      【讨论】:

        【解决方案5】:

        简短的回答:你不能

        这是因为即使您可以想象序列化方案,.NET Serializer 也不能保证重新序列化的键将具有相同的哈希值。

        长答案:可以,但工作量很大

        您的解决方案是对整个对象进行手动序列化,或者将 Dictionary 替换为一个简单的包装器属性,该包装器属性包装IDictionary,但将其公开为对列表。 (如果需要,可以稍后转换。)

        但是:这会留下与上述相同的问题,您不能保证重新序列化的密钥将具有与序列化之前相同的哈希码。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-08-19
          • 1970-01-01
          • 1970-01-01
          • 2013-03-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多