我正在尝试将一些较旧的作品转换为使用Newtonsoft JSON.NET。使用System.Web.Script.Serialization.JavaScriptSerializer.Deserialize
方法的默认处理(例如,如果未指定目标类型)是为内部对象返回Dictionary<string,object>
。
这实际上是JSON非常有用的基本类型,因为它也恰好是ExpandoObjects
使用的基础类型,是动态类型最明智的内部实现。
如果我指定这种类型,例如:
var dict = JsonConvert.DeserializeObject<Dictionary<string,object>>(json);
JSON.NET将正确地反序列化最外层的对象结构,但它为任何内部结构返回JObject
类型。我真正需要的是将相同的外部结构用于任何内部对象类型结构。
有没有办法指定一个用于内部对象的类型,而不仅仅是返回的最外层类型?
答案 0 :(得分:18)
为了让Json.Net将json字符串反序列化为IDictionary<string, object>
,包括反序列化嵌套对象和数组,您需要创建一个自定义类,该类派生自Json提供的JsonConverter
抽象类.NET。
在您的派生JsonConverter
中,您可以在其中执行如何在json中写入对象的实现。
您可以像这样使用自定义JsonConverter
:
var o = JsonConvert.DeserializeObject<IDictionary<string, object>>(json, new DictionaryConverter());
以下是我过去成功使用的自定义JsonConverter,以实现与您在问题中概述的目标相同的目标:
public class DictionaryConverter : JsonConverter {
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { this.WriteValue(writer, value); }
private void WriteValue(JsonWriter writer, object value) {
var t = JToken.FromObject(value);
switch (t.Type) {
case JTokenType.Object:
this.WriteObject(writer, value);
break;
case JTokenType.Array:
this.WriteArray(writer, value);
break;
default:
writer.WriteValue(value);
break;
}
}
private void WriteObject(JsonWriter writer, object value) {
writer.WriteStartObject();
var obj = value as IDictionary<string, object>;
foreach (var kvp in obj) {
writer.WritePropertyName(kvp.Key);
this.WriteValue(writer, kvp.Value);
}
writer.WriteEndObject();
}
private void WriteArray(JsonWriter writer, object value) {
writer.WriteStartArray();
var array = value as IEnumerable<object>;
foreach (var o in array) {
this.WriteValue(writer, o);
}
writer.WriteEndArray();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
return ReadValue(reader);
}
private object ReadValue(JsonReader reader) {
while (reader.TokenType == JsonToken.Comment) {
if (!reader.Read()) throw new JsonSerializationException("Unexpected Token when converting IDictionary<string, object>");
}
switch (reader.TokenType) {
case JsonToken.StartObject:
return ReadObject(reader);
case JsonToken.StartArray:
return this.ReadArray(reader);
case JsonToken.Integer:
case JsonToken.Float:
case JsonToken.String:
case JsonToken.Boolean:
case JsonToken.Undefined:
case JsonToken.Null:
case JsonToken.Date:
case JsonToken.Bytes:
return reader.Value;
default:
throw new JsonSerializationException
(string.Format("Unexpected token when converting IDictionary<string, object>: {0}", reader.TokenType));
}
}
private object ReadArray(JsonReader reader) {
IList<object> list = new List<object>();
while (reader.Read()) {
switch (reader.TokenType) {
case JsonToken.Comment:
break;
default:
var v = ReadValue(reader);
list.Add(v);
break;
case JsonToken.EndArray:
return list;
}
}
throw new JsonSerializationException("Unexpected end when reading IDictionary<string, object>");
}
private object ReadObject(JsonReader reader) {
var obj = new Dictionary<string, object>();
while (reader.Read()) {
switch (reader.TokenType) {
case JsonToken.PropertyName:
var propertyName = reader.Value.ToString();
if (!reader.Read()) {
throw new JsonSerializationException("Unexpected end when reading IDictionary<string, object>");
}
var v = ReadValue(reader);
obj[propertyName] = v;
break;
case JsonToken.Comment:
break;
case JsonToken.EndObject:
return obj;
}
}
throw new JsonSerializationException("Unexpected end when reading IDictionary<string, object>");
}
public override bool CanConvert(Type objectType) { return typeof(IDictionary<string, object>).IsAssignableFrom(objectType); }
}
答案 1 :(得分:7)
使用Json反序列化复杂对象时,需要添加JsonSerializer设置作为参数。这将确保所有内部类型都能正确反序列化。
private JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Full
};
序列化对象时,可以使用SerializerSettings:
string json= JsonConvert.SerializeObject(myObject, _jsonSettings)
然后在反序列化时,使用:
var dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, _jsonSettings);
此外,序列化时,将JsonSerializerSettings添加到SerializeObject(对象,设置)
编辑:如果需要,您还可以更改TypeNameHandling和TypeNameAssemblyFormat。我将它们分别设置为'All'和'Full'以确保我的复杂对象毫无疑问地被序列化和反序列化,但是intellisense为您提供了其他选择