我正在使用DataContractSerializer
序列化包含Dictionary<string,object>
成员的对象,该成员标有[DataMember()]
。我们的想法是拥有一个灵活的对象属性包,我不知道这些属性是什么。
当我将int
,double
和string
个对象放入字典时,这很有用,但当我在其中放置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>
或任何东西。
处理这种情况的正确方法是什么?
答案 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;
}
}