问题描述
我有一个使用 DataContractJsonSerializer 序列化到存储的字典,我想使用 Newtonsoft.Json 对其进行反序列化.
I have a dictionary serialized to storage with DataContractJsonSerializer which I would like to deserialize with Newtonsoft.Json.
DataContractJsonSerializer 已将 Dictionary 序列化为键/值对列表:
The DataContractJsonSerializer has serialized the Dictionary to a list of Key/Value pairs:
{"Dict":[{"Key":"Key1","Value":"Val1"},{"Key":"Key2","Value":"Val2"}]}
我可以为 JsonConvert.DeserializeObject<>()
提供什么很酷的选项,使其支持该数据格式和来自 Newtonsoft.Json 的格式?
Is there any cool options I can give the JsonConvert.DeserializeObject<>()
that will make it support both that data format and the format from Newtonsoft.Json?
{"Dict":{"Key1":"Val1","Key2":"Val2"}}
是 Newtonsoft.Json 创建的漂亮格式,我希望在过渡期间能够同时读取旧的 DataContract 格式和新的 Newtonsoft 格式.
Is the pretty format Newtonsoft.Json creates, and I would like to be able to read both the old DataContract format and the new Newtonsoft format in a transition period.
简化示例:
//[JsonArray]
public sealed class Data
{
public IDictionary<string, string> Dict { get; set; }
}
[TestMethod]
public void TestSerializeDataContractDeserializeNewtonsoftDictionary()
{
var d = new Data
{
Dict = new Dictionary<string, string>
{
{"Key1", "Val1"},
{"Key2", "Val2"},
}
};
var oldJson = String.Empty;
var formatter = new DataContractJsonSerializer(typeof (Data));
using (var stream = new MemoryStream())
{
formatter.WriteObject(stream, d);
oldJson = Encoding.UTF8.GetString(stream.ToArray());
}
var newJson = JsonConvert.SerializeObject(d);
// [JsonArray] on Data class gives:
//
// System.InvalidCastException: Unable to cast object of type 'Data' to type 'System.Collections.IEnumerable'.
Console.WriteLine(oldJson);
// This is tha data I have in storage and want to deserialize with Newtonsoft.Json, an array of key/value pairs
// {"Dict":[{"Key":"Key1","Value":"Val1"},{"Key":"Key2","Value":"Val2"}]}
Console.WriteLine(newJson);
// This is what Newtonsoft.Json generates and should also be supported:
// {"Dict":{"Key1":"Val1","Key2":"Val2"}}
var d2 = JsonConvert.DeserializeObject<Data>(newJson);
Assert.AreEqual("Val1", d2.Dict["Key1"]);
Assert.AreEqual("Val2", d2.Dict["Key2"]);
var d3 = JsonConvert.DeserializeObject<Data>(oldJson);
// Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into
// type 'System.Collections.Generic.IDictionary`2[System.String,System.String]' because the type requires a JSON
// object (e.g. {"name":"value"}) to deserialize correctly.
//
// To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type
// to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be
// deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from
// a JSON array.
//
// Path 'Dict', line 1, position 9.
Assert.AreEqual("Val1", d3.Dict["Key1"]);
Assert.AreEqual("Val2", d3.Dict["Key2"]);
}
推荐答案
您可以为此使用自定义转换器,具体取决于字典以什么标记开头,将其反序列化为 JSON.NET 的默认方式,或将其反序列化为数组并然后将该数组转换为 Dictionary
:
You could use a custom converter for this, depending on what token the dictionary starts with, deserialize it JSON.NET's default way, or deserialize it into an array and then turn that array into a Dictionary
:
public class DictionaryConverter : JsonConverter
{
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
IDictionary<string, string> result;
if (reader.TokenType == JsonToken.StartArray)
{
JArray legacyArray = (JArray)JArray.ReadFrom(reader);
result = legacyArray.ToDictionary(
el => el["Key"].ToString(),
el => el["Value"].ToString());
}
else
{
result =
(IDictionary<string, string>)
serializer.Deserialize(reader, typeof(IDictionary<string, string>));
}
return result;
}
public override void WriteJson(
JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(IDictionary<string, string>).IsAssignableFrom(objectType);
}
public override bool CanWrite
{
get { return false; }
}
}
然后,您可以使用 JsonConverter
属性来装饰 Data
类中的 Dict
属性:
Then, you can decorate the Dict
property in the Data
class with a JsonConverter
attribute:
public sealed class Data
{
[JsonConverter(typeof(DictionaryConverter))]
public IDictionary<string, string> Dict { get; set; }
}
然后反序列化两个字符串应该按预期工作.
Then deserializing both strings should work as expected.
这篇关于Newtonsoft Json 将字典反序列化为来自 DataContractJsonSerializer 的键/值列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!