读取</string,object>时,DataContractSerializer和Dictionary <string,object>失败

时间:2011-10-01 14:05:58

标签: c# .net generics datacontractserializer

我正在使用DataContractSerializer序列化包含Dictionary<string,object>成员的对象,该成员标有[DataMember()]。我们的想法是拥有一个灵活的对象属性包,我不知道这些属性是什么。

当我将intdoublestring个对象放入字典时,这很有用,但当我在其中放置List<string>时,无法反序列化对象用:

System.InvalidOperationException: Node type Element is not supported in this operation.

整个字典被序列化为XML,看起来很合理:

<Attributes xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>name</d2p1:Key>
        <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:string">Test object</d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>x</d2p1:Key>
        <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:double">0.5</d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>y</d2p1:Key>
        <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:double">1.25</d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>age</d2p1:Key>
        <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:int">4</d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
    <d2p1:KeyValueOfstringanyType>
        <d2p1:Key>list-of-strings</d2p1:Key>
        <d2p1:Value>
            <d2p1:string>one string</d2p1:string>
            <d2p1:string>two string</d2p1:string>
            <d2p1:string>last string</d2p1:string>
        </d2p1:Value>
    </d2p1:KeyValueOfstringanyType>
</Attributes>

注意那里的list-of-strings。它有所有的值,但没有任何表明它是List<string>或任何东西。

处理这种情况的正确方法是什么?

3 个答案:

答案 0 :(得分:9)

尝试使用KnownTypeAttribute,以便DataContractSerializer了解List<string>类型。不幸的是,这似乎违背了你不必事先知道类型的想法。

我基于以下代码,该代码使用DataContractSerializer序列化包含Dictionary<string, object>的{​​{1}}:

List<string>

如果Dictionary<string,object> dictionary = new Dictionary<string, object>(); dictionary.Add("k1", new List<string> { "L1", "L2", "L3" }); List<Type> knownTypes = new List<Type> { typeof(List<string>) }; DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string,object>), knownTypes); MemoryStream stream = new MemoryStream(); serializer.WriteObject(stream, dictionary); StreamReader reader = new StreamReader(stream); stream.Position = 0; string xml = reader.ReadToEnd(); 未提供knownTypes,则会引发异常。

  

SerializationException:Type   'System.Collections.Generic.List`1 [[System.String,mscorlib,   Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089]]'   与数据合同名称   'ArrayOfstring:HTTP://schemas.microsoft.com/2003/10/Serialization/Arrays'   不是预期的。考虑使用DataContractResolver或添加任何   静态地知道已知类型列表的类型 - 例如,   通过使用KnownTypeAttribute属性或将它们添加到   传递给DataContractSerializer的已知类型列表。

答案 1 :(得分:0)

WCF无法知道你所拥有的是List<string> - 请注意所有其他<Value>元素都有“类型提示”(i:type属性)。如果你想反序列化它,它需要有标记,你还需要告诉WCF List<string>是一个“已知类型” - 见下文。有关已知类型的详细信息(以及需要它们的原因),many中有good resources web

public class StackOverflow_7620718
{
    public static void Test()
    {
        Dictionary<string, object> dict = new Dictionary<string, object>
        {
            { "name", "Test object" },
            { "x", 0.5 },
            { "y", 1.25 },
            { "age", 4 },
            { "list-of-strings", new List<string> { "one string", "two string", "last string" } }
        };
        MemoryStream ms = new MemoryStream();
        XmlWriter w = XmlWriter.Create(ms, new XmlWriterSettings
        {
            Indent = true,
            Encoding = new UTF8Encoding(false),
            IndentChars = "  ",
            OmitXmlDeclaration = true,
        });
        DataContractSerializer dcs = new DataContractSerializer(dict.GetType(), new Type[] { typeof(List<string>) });
        dcs.WriteObject(w, dict);
        w.Flush();
        Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
        ms.Position = 0;
        Console.WriteLine("Now deserializing it:");
        Dictionary<string, object> dict2 = (Dictionary<string, object>)dcs.ReadObject(ms);
        foreach (var key in dict2.Keys)
        {
            Console.WriteLine("{0}: {1}", key, dict2[key].GetType().Name);
        }
    }
}

答案 2 :(得分:0)

我有类似的想法次数,并且通过附加字段保存所有Dictionary项的程序集合格名称来实现它。它会在每次添加或重写项时填充列表,然后在序列化时使用它,并使用XmlReader提取类型信息,构建类型列表并反序列化对象。

代码:

[DataContract]
public class Message
{
    [DataMember] private List<string> Types = new List<string>();
    [DataMember] private Dictionary<string, object> Data = new Dictionary<string, object>();

    public object this[string id]
    {
        get => Data.TryGetValue(id, out var o) ? o : null;
        set {
            Data[id] = value;
            if (!Types.Contains(value.GetType().AssemblyQualifiedName))
                Types.Add(value.GetType().AssemblyQualifiedName);
        }
    }

    public byte[] Serialize()
    {
        var dcs = new DataContractSerializer(typeof(Message), Types.Select(Type.GetType));
        using (var ms = new MemoryStream()) {
            dcs.WriteObject(ms, this);
            return ms.ToArray();
        }
    }

    public static Message Deserialize(byte[] input)
    {
        var types = new List<string>();
        using (var xr = XmlReader.Create(new StringReader(Encoding.UTF8.GetString(input)))) {
            if (xr.ReadToFollowing(nameof(Types))) {
                xr.ReadStartElement();
                while (xr.NodeType != XmlNodeType.EndElement) {
                    var res = xr.ReadElementContentAsString();
                    if (!string.IsNullOrWhiteSpace(res))
                        types.Add(res);
                }
            }
        }
        var dcs = new DataContractSerializer(typeof(Message), types.Select(Type.GetType));
        using (var ms = new MemoryStream(input))
            if (dcs.ReadObject(ms) is Message msg)
                return msg;
        return null;
    }
}