【问题标题】:Using Json.net to deserialize to any unkown type works for objects but not value types使用 Json.net 反序列化为未知类型适用于对象但不适用于值类型
【发布时间】:2016-04-07 17:41:11
【问题描述】:

我已经实现了一个基于 Json.net 的 Json 序列化器来接受任何对象类型并将其序列化(以便放置到我的缓存中)

缓存接口不允许我指定类型,所以当我从缓存中检索时,我需要根据元信息动态创建类型。

这适用于对象,我现在面临的问题是我不适用于值类型,我会得到一个例外,说类似cannot cast JValue to JObject

我的问题是如何同时满足值类型和对象类型的需求?如果有一个用于 JObject 的 TryParse 那就太好了,我可以自己编写它,但感觉就像我要掉进兔子洞一样?

实现这一目标的最佳方法是什么?

我的代码如下,Json.net的设置:

_settings = new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver(),
                NullValueHandling = NullValueHandling.Ignore,
                DateTimeZoneHandling = DateTimeZoneHandling.Utc,
                TypeNameHandling = TypeNameHandling.All
            };

_settings.Converters.Add(new StringEnumConverter());

设置函数(序列化):

public void Put(string cacheKey, object toBeCached, TimeSpan cacheDuration)
        {
            _cache.Set(cacheKey, JsonConvert.SerializeObject(toBeCached, _settings), cacheDuration);
        }

还有get(反序列化):

 public object Get(string cacheKey)
    {
        try
        {
            var value = _cache.Get(cacheKey);

            if (!value.HasValue)
            {
                return null;
            }

            var jobject = JsonConvert.DeserializeObject<JObject>(value);
            var typeName = jobject?["$type"].ToString();

            if (typeName == null)
            {
                return null;
            }

            var type = Type.GetType(typeName);
            return jobject.ToObject(type);
        }
        catch (Exception e)
        {
            // Todo
            return null;
        }
    }

【问题讨论】:

    标签: c# serialization json.net


    【解决方案1】:

    您需要解析为JToken 而不是JObject,然后检查返回的类型是否为包含JSON 原语的JValue

    public static object Get(string value)
    {
        var jToken = JsonConvert.DeserializeObject<JToken>(value);
        if (jToken == null)
            return null;
        else if (jToken is JValue)
        {
            return ((JValue)jToken).Value;
        }
        else
        {
            if (jToken["$type"] == null)
                return null;
            // Use the same serializer settings as used during serialization.
            // Ideally with a proper SerializationBinder that sanitizes incoming types as suggested
            // in https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_TypeNameHandling.htm
            var _settings = new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver(),
                NullValueHandling = NullValueHandling.Ignore,
                DateTimeZoneHandling = DateTimeZoneHandling.Utc,
                TypeNameHandling = TypeNameHandling.All,
                Converters = { new StringEnumConverter() },
                //SerializationBinder = new SafeSerializationBinder(),
            };
            // Since the JSON contains a $type parameter and TypeNameHandling is enabled, if we deserialize 
            // to type object the $type information will be used to determine the actual type, using Json.NET's
            // serialization binder: https://www.newtonsoft.com/json/help/html/SerializeSerializationBinder.htm
            return jToken.ToObject(typeof(object), JsonSerializer.CreateDefault(_settings));
        }
    }
    

    但是请注意,原语的类型信息不会精确地往返:

    如果您需要为基元往返类型信息,请考虑使用来自Deserialize specific enum into system.enum in Json.NetTypeWrapper&lt;T&gt; 来封装您的根对象。

    最后,如果您有可能反序列化不受信任的 JSON(如果您是从文件或互联网反序列化,那么您肯定是),请注意Json.NET documentation 的以下警告:

    当您的应用程序从外部源反序列化 JSON 时,应谨慎使用 TypeNameHandling。 在使用 None 以外的值反序列化时,应使用自定义 SerializationBinder 验证传入类型。

    有关为什么这可能是必要的讨论,请参阅 TypeNameHandling caution in Newtonsoft JsonHow to configure Json.NET to create a vulnerable web API 和 Alvaro Muñoz 和 Oleksandr Mirosh 的黑帽论文 https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf

    【讨论】:

    • 这是我见过的最易受攻击的 JSON 反序列化程序之一。您允许攻击者从字面上反序列化任何类型,包括具有副作用构造函数的类型。至少使用一个开关并将选项缩小到相关的选项。不像去酸洗那么糟糕,但该死的。
    • @cormacrelf - 你知道,你说得有道理。我刚刚回答了 我正在使用多态 $type 属性反序列化 JSON 的确切问题。如何扩展它以反序列化原始值?没有考虑这是否是一个好主意。唯一的防御是应用程序本身,而不是某个外部调用者,正在将对象序列化到它的缓存中。但至少有一个指向TypeNameHandling caution in Newtonsoft Json 的链接是合适的。我已经更新了一些答案以链接到它。
    • 好好。我有点苛刻,但这些东西被复制粘贴了(这个问题有 268 次浏览),必须清楚!
    • 更好的答案。将滑索直接带到最佳实践参考。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多