问题描述
我有一个序列化为DataContractJsonSerializer存储的字典,我想使用Newtonsoft.Json反序列化。DataContractJsonSerializer将Dictionary序列化为Key /价值对:
{Dict:[{Key:Key1,Value:Val1} ,{Key:Key2,Value:Val2}]}
是有什么很酷的选择,我可以给 JsonConvert.DeserializeObject<>()
,这将使它支持数据格式和格式从Newtonsoft.Json?
{Dict:{Key1:Val1,Key2:Val2}}
Newtonsoft.Json创建的漂亮格式,我想要能够读取旧的DataContract格式和新的Newtonsoft格式在一个过渡期。
简化示例:
// [ JsonArray]
public sealed class Data
{
public IDictionary< string,string& GT; Dict {get;组;
}
[TestMethod]
public void TestSerializeDataContractDeserializeNewtonsoftDictionary()
{
var d = new Data
{
Dict =新词典< 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]给出:
//
// System.InvalidCastException:无法将类型为Data的对象转换为键入System.Collections.IEnumerable。
Console.WriteLine(oldJson);
//这是我存储的数据,并希望使用Newtonsoft.Json反序列化一个键/值对数组
//Dict:[{Key:Key1 ,Value:Val1},{Key:Key2,Value:Val2}}}
Console.WriteLine(newJson);
//这是Newtonsoft.Json生成的,也应该被支持:
// {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:无法反序列化当前的JSON数组(例如[1,2,3])到
//键入'System.Collections.Generic.IDictionary`2 [System.String ,System.String]',因为该类型需要一个JSON
//对象(例如{name:value})来正确反序列化。
//
//要修复此错误,请将JSON更改为JSON对象(例如{name:value})或将反序列化类型
//更改为数组或者实现例如List< T>的收集接口(例如,ICollection,IList)的类型。这可以是
//从JSON数组中反序列化。 JsonArrayAttribute也可以添加到类型,以强制它从
//一个JSON数组反序列化。
//
//路径'Dict',行1,位置9.
Assert.AreEqual(Val1,d3.Dict [Key1]);
Assert.AreEqual(Val2,d3.Dict [Key2]);
}
您可以使用自定义转换器为此,取决于字典开始的标志,反序列化JSON.NET的默认方式,或将其反序列化为数组,然后将该数组转换为字典
:
public class DictionaryConverter:JsonConverter
{
public override object ReadJson(
JsonReader reader,
键入objectType,
object existingValue,
JsonSerializer serializer)
{
IDictionary< string,string>结果;
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> ;));
}
返回结果;
}
public override void WriteJson(
JsonWriter writer,对象值,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; }
}
}
然后,您可以装饰<$ c $在数据
中,具有 JsonConverter
属性的类别属性
public sealed class Data
{
[JsonConverter(typeof(DictionaryConverter))]
public IDictionary< string ,字符串> Dict {get;组; }
}
然后反序列化这两个字符串应该按预期工作。
I have a dictionary serialized to storage with DataContractJsonSerializer which I would like to deserialize with Newtonsoft.Json.
The DataContractJsonSerializer has serialized the Dictionary to a list of Key/Value pairs:
{"Dict":[{"Key":"Key1","Value":"Val1"},{"Key":"Key2","Value":"Val2"}]}
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"}}
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.
Simplified example:
//[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"]);
}
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; }
}
}
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 Deserialize Dictionary从DataContractJsonSerializer的键/值列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!