Jør*_*vik 16 c# json dictionary json.net deserialization
我有一个字典序列化存储与DataContractJsonSerializer,我想用Newtonsoft.Json反序列化.
DataContractJsonSerializer已将Dictionary序列化为键/值对列表:
{"Dict":[{"Key":"Key1","Value":"Val1"},{"Key":"Key2","Value":"Val2"}]}
Run Code Online (Sandbox Code Playgroud)
有什么很酷的选择,我可以给它JsonConvert.DeserializeObject<>(),它将支持数据格式和Newtonsoft.Json的格式?
{"Dict":{"Key1":"Val1","Key2":"Val2"}}
Run Code Online (Sandbox Code Playgroud)
是Newtonsoft.Json创建的漂亮格式,我希望能够在过渡期内同时读取旧的DataContract格式和新的Newtonsoft格式.
简化示例:
//[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"]);
}
Run Code Online (Sandbox Code Playgroud)
dbc*_*dbc 22
扩展Andrew Whitaker的答案,这是一个完全通用的版本,适用于任何类型的可写字典:
public class JsonGenericDictionaryOrArrayConverter: JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.GetDictionaryKeyValueTypes().Count() == 1;
}
public override bool CanWrite { get { return false; } }
object ReadJsonGeneric<TKey, TValue>(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tokenType = reader.TokenType;
var dict = existingValue as IDictionary<TKey, TValue>;
if (dict == null)
{
var contract = serializer.ContractResolver.ResolveContract(objectType);
dict = (IDictionary<TKey, TValue>)contract.DefaultCreator();
}
if (tokenType == JsonToken.StartArray)
{
var pairs = new JsonSerializer().Deserialize<KeyValuePair<TKey, TValue>[]>(reader);
if (pairs == null)
return existingValue;
foreach (var pair in pairs)
dict.Add(pair);
}
else if (tokenType == JsonToken.StartObject)
{
// Using "Populate()" avoids infinite recursion.
// https://github.com/JamesNK/Newtonsoft.Json/blob/ee170dc5510bb3ffd35fc1b0d986f34e33c51ab9/Src/Newtonsoft.Json/Converters/CustomCreationConverter.cs
serializer.Populate(reader, dict);
}
return dict;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var keyValueTypes = objectType.GetDictionaryKeyValueTypes().Single(); // Throws an exception if not exactly one.
var method = GetType().GetMethod("ReadJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
var genericMethod = method.MakeGenericMethod(new[] { keyValueTypes.Key, keyValueTypes.Value });
return genericMethod.Invoke(this, new object [] { reader, objectType, existingValue, serializer } );
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static class TypeExtensions
{
/// <summary>
/// Return all interfaces implemented by the incoming type as well as the type itself if it is an interface.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
{
if (type == null)
throw new ArgumentNullException();
if (type.IsInterface)
return new[] { type }.Concat(type.GetInterfaces());
else
return type.GetInterfaces();
}
public static IEnumerable<KeyValuePair<Type, Type>> GetDictionaryKeyValueTypes(this Type type)
{
foreach (Type intType in type.GetInterfacesAndSelf())
{
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
var args = intType.GetGenericArguments();
if (args.Length == 2)
yield return new KeyValuePair<Type, Type>(args[0], args[1]);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后就像使用它一样
var settings = new JsonSerializerSettings { Converters = new JsonConverter[] {new JsonGenericDictionaryOrArrayConverter() } };
var d2 = JsonConvert.DeserializeObject<Data>(newJson, settings);
var d3 = JsonConvert.DeserializeObject<Data>(oldJson, settings);
Run Code Online (Sandbox Code Playgroud)
您可以使用自定义转换器,具体取决于字典开头的标记,将其反序列化为JSON.NET的默认方式,或者将其反序列化为数组,然后将该数组转换为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; }
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用Dict属性装饰Data类中的JsonConverter属性:
public sealed class Data
{
[JsonConverter(typeof(DictionaryConverter))]
public IDictionary<string, string> Dict { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
然后反序列化两个字符串应该按预期工作.