DataContractSerializer和Dictionary 读书时失败了

问题描述 投票:7回答:3

我正在使用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>或任何东西。

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

c# .net generics datacontractserializer
3个回答
9
投票

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

我基于以下代码,它使用DataContractSerializer序列化包含Dictionary<string, object>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提供DataContractSerializer,它会引发异常。

SerializationException:键入'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的已知类型列表中。


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);
        }
    }
}

0
投票

我有一个类似的想法,并且我使用所有词典项目的附加字段持有程序集限定名称来实现它。它在每个项目添加或重写时填充列表,然后在序列化时使用它,并使用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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.