Десериализовать JSON рекурсивно для IDictionary <string, object>
Я пытаюсь преобразовать некоторые старые работы для использования 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
для любых внутренних структур. Мне действительно нужно, чтобы одна и та же внешняя структура использовалась для любых структур внутреннего объекта.
Есть ли способ указать тип, который будет использоваться для внутренних объектов, а не только внешний вид?
Ответы
Ответ 1
При десериализации сложных объектов с помощью 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, если вам нужно. Я установил их "Все" и "Полный", чтобы гарантировать, что мои сложные объекты будут сериализованы и десериализованы правильно, но intellisense предоставит вам другие альтернативы.
Ответ 2
Чтобы Json.Net десериализовал строку json в IDictionary<string, object>
, включая десериализацию вложенных объектов и массивов, вам нужно создать собственный класс, который происходит из абстрактного класса JsonConverter
, предоставленного Json.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); }
}