我已经实现了一个基于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;
}
}
答案 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));
}
}
但请注意,基元的类型信息不会精确地往返:
整数将以long
(或大于long.MaxValue
的BigInteger
)的形式返回。
浮点值将以double
或decimal
的形式返回,具体取决于JsonSerializerSettings.FloatParseHandling
的设置 - FloatParseHandling.Double
或{{3} }。
“看起来像”日期的JSON字符串将转换为DateTime
,DateTimeOffset
或保留为字符串,具体取决于设置FloatParseHandling.Decimal
。
enum
名称将转换为字符串。
如果您需要基元的往返类型信息,请考虑使用JsonSerializerSettings.DateParseHandling
中的TypeWrapper<T>
来封装您的根对象。
最后,如果您有可能将不受信任的JSON反序列化(如果您从文件或互联网进行反序列化,那么您肯定是),请注意Deserialize specific enum into system.enum in Json.Net中的以下注意事项:
当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。 使用非None以外的值进行反序列化时,应使用自定义Json.NET documentation验证传入类型。
有关为何需要这样做的讨论,请参阅SerializationBinder,TypeNameHandling caution in Newtonsoft Json和AlvaroMuñoz&amp; Oleksandr Mirosh的黑帽纸How to configure Json.NET to create a vulnerable web API