DataContractSerializer 和 Dictionary读取时失败

发布于 2024-12-07 07:13:20 字数 2105 浏览 0 评论 0原文

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

当我将 intdoublestring 对象放入字典中时,这非常有用,但是当我放入 List时,效果很好; 其中无法反序列化对象:

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 或任何内容。

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

I'm using DataContractSerializer to serialize an object that contains a Dictionary<string,object> member, which is marked with [DataMember()]. The idea is to have a flexible bag of object attributes, and I don't know what those attributes could be.

This works great when I'm putting int, double and string objects into the dictionary, but when I put a List<string> in it fails to deserialize the object with:

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

The entire dictionary is serialized to XML, and it looks pretty reasonable:

<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>

Note the list-of-strings at the end there. It's got all the values but nothing indicating that it's a List<string> or anything.

What's the correct way of handling this situation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

关于从前 2024-12-14 07:13:20

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

我基于以下代码,该代码使用 DataContractSerializer 序列化包含 ListDictionary

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,
版本=4.0.0.0,文化=中性,PublicKeyToken=b77a5c561934e089]]'
带有数据合约名称
'ArrayOfstring:http://schemas.microsoft.com/2003/10/Serialization/Arrays'
预计不会。考虑使用 DataContractResolver 或添加任何
已知类型列表中静态未知的类型 - 例如,
通过使用 KnownTypeAttribute 属性或将它们添加到
传递给 DataContractSerializer 的已知类型列表。

Try using the KnownTypeAttribute so that DataContractSerializer knows about the List<string> type. Unfortunately, that seems to go against your idea of not having to know about the types before hand.

I'm basing this on the following code, which uses DataContractSerializer to serialize a Dictionary<string, object> containing 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();

If you knownTypes is not provided to the DataContractSerializer, it throws an exception.

SerializationException: Type
'System.Collections.Generic.List`1[[System.String, mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'
with data contract name
'ArrayOfstring:http://schemas.microsoft.com/2003/10/Serialization/Arrays'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.

不离久伴 2024-12-14 07:13:20

WCF 无法知道您拥有的是一个 List - 请注意,所有其他 元素都有一个“类型提示”( i:类型属性)。如果您想反序列化它,它需要有标记,并且您还需要告诉 WCF List 是“已知类型” - 见下文。有关已知类型(以及为什么需要它们)的更多信息,有许多< /a> 资源网络

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

WCF has no way of knowing that what you have is a List<string> there - notice that all the other <Value> elements have a "type hint" (the i:type attribute). If you want to deserialize it, it needs to have the marking, and you also need to tell WCF that List<string> is a "known type" - see below. For more information on known types (and why they're needed) there are many good resources in the 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);
        }
    }
}
抚笙 2024-12-14 07:13:20

我多次有类似的想法,并且我使用附加字段来实现它,该字段保存所有字典项的程序集限定名称。它在每个项目添加或重写时填充列表,然后在序列化时使用它,并使用 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;
    }
}

I had a similiar idea number of times, and I implemented it with additional field holding assembly qualified names of all Dictionary items. It populates list on every item addition or rewrite, then uses it on serialization and uses XmlReader to extract type information, build list of types and deserialize object.

Code:

[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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文