将JSON递归反序列化为IDictionary <string,object>

Jam*_*rgy 16 c# json json.net

我正在尝试将一些较旧的作品转换为使用Newtonsoft JSON.NET.使用该System.Web.Script.Serialization.JavaScriptSerializer.Deserialize方法的默认处理(例如,如果未指定目标类型)是Dictionary<string,object>为内部对象返回a .

这实际上是JSON非常有用的基本类型,因为它也恰好ExpandoObjects是动态类型使用的基础类型,也是最合理的动态类型内部实现.

如果我指定此类型,例如:

 var dict = JsonConvert.DeserializeObject<Dictionary<string,object>>(json);
Run Code Online (Sandbox Code Playgroud)

JSON.NET将正确地反序列化最外层的对象结构,但它返回JObject任何内部结构的类型.我真正需要的是将相同的外部结构用于任何内部对象类型结构.

有没有办法指定一个用于内部对象的类型,而不仅仅是返回的最外层类型?

Ani*_*tel 21

为了让Json.Net将json字符串反序列化为IDictionary<string, object>包括反序列化嵌套对象和数组,您需要创建一个自定义类,该类派生自JsonConverterJson.Net提供的抽象类.

在你的派生中JsonConverter,你将如何写入和从json写入对象的实现.

您可以JsonConverter像这样使用自定义:

var o = JsonConvert.DeserializeObject<IDictionary<string, object>>(json, new DictionaryConverter());
Run Code Online (Sandbox Code Playgroud)

这是我过去成功使用的自定义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); }
}
Run Code Online (Sandbox Code Playgroud)

  • 这可以通过覆盖 `CanWrite` 并返回 false 来简化,如[如何在自定义 JsonConverter 中使用默认序列化](/sf/ask/2073161751/) 中所示。完成后,您可以从 WriteJson 抛出异常,因为它不会被调用。 (2认同)

Mic*_*ren 5

使用Json反序列化复杂对象时,需要添加JsonSerializer设置作为参数.这将确保所有内部类型都能正确反序列化.

    private JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.All,
        TypeNameAssemblyFormat = FormatterAssemblyStyle.Full
    };
Run Code Online (Sandbox Code Playgroud)

序列化对象时,可以使用SerializerSettings:

    string json= JsonConvert.SerializeObject(myObject, _jsonSettings)
Run Code Online (Sandbox Code Playgroud)

然后在反序列化时,使用:

    var dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, _jsonSettings);
Run Code Online (Sandbox Code Playgroud)

此外,序列化时,将JsonSerializerSettings添加到SerializeObject(对象,设置)

编辑:如果需要,您还可以更改TypeNameHandling和TypeNameAssemblyFormat.我将它们分别设置为'All'和'Full',以确保我的复杂对象毫无疑问地被序列化和反序列化,但是intellisense为您提供了其他选择

  • @LukePuplett根据提出的问题,我认为这是不正确的。 (2认同)