使用Json.net反序列化为任何未知类型适用于对象但不适用于值类型

时间:2016-01-04 06:09:55

标签: c# serialization json.net

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

缓存接口不允许我对类型进行speficy,因此当我从缓存中检索时,我需要从元信息中动态创建类型。

哪种方法适用于对象,我现在面临的问题是我不适用于值类型,我会得到一个例外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;
        }
    }

1 个答案:

答案 0 :(得分: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));
    }
}

但请注意,基元的类型信息不会精确地往返:

如果您需要基元的往返类型信息,请考虑使用JsonSerializerSettings.DateParseHandling中的TypeWrapper<T>来封装您的根对象。

最后,如果您有可能将不受信任的JSON反序列化(如果您从文件或互联网进行反序列化,那么您肯定是),请注意Deserialize specific enum into system.enum in Json.Net中的以下注意事项:

  

当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。 使用非None以外的值进行反序列化时,应使用自定义Json.NET documentation验证传入类型。

有关为何需要这样做的讨论,请参阅SerializationBinderTypeNameHandling caution in Newtonsoft Json和AlvaroMuñoz&amp; Oleksandr Mirosh的黑帽纸How to configure Json.NET to create a vulnerable web API