我正在使用Newtonsoft.Json程序集将Json字符串反序列化为动态对象(ExpandoObject)。我遇到的问题是int值总是作为Int64返回,我期待Int32。代码可以在下面看到。
namespace Serialization
{
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static class JsonSerializer
{
#region Public Methods
public static string Serialize(dynamic obj)
{
return JsonConvert.SerializeObject(obj);
}
public static dynamic Deserialize(string s)
{
var obj = JsonConvert.DeserializeObject(s);
return obj is string ? obj as string : Deserialize((JToken)obj);
}
#endregion
#region Methods
private static dynamic Deserialize(JToken token)
{
// FROM : http://blog.petegoo.com/archive/2009/10/27/using-json.net-to-eval-json-into-a-dynamic-variable-in.aspx
// Ideally in the future Json.Net will support dynamic and this can be eliminated.
if (token is JValue) return ((JValue)token).Value;
if (token is JObject)
{
var expando = new ExpandoObject();
(from childToken in token
where childToken is JProperty
select childToken as JProperty).ToList().
ForEach(property => ((IDictionary<string, object>)expando).Add(property.Name, Deserialize(property.Value)));
return expando;
}
if (token is JArray)
{
var items = new List<object>();
foreach (var arrayItem in ((JArray)token)) items.Add(Deserialize(arrayItem));
return items;
}
throw new ArgumentException(string.Format("Unknown token type '{0}'", token.GetType()), "token");
}
#endregion
}
}
通常我不会注意到这一点,但是这个特殊的int在反射中用于某些类型检查,并且它失败了。任何想法为什么会发生这种情况将不胜感激。
答案 0 :(得分:3)
交叉链接以回答https://stackoverflow.com/a/9444519/1037948
来自How do I change the default Type for Numeric deserialization?
意译:
Int64
返回以避免溢出错误,并且更容易检查(对于Json.NET内部,而不是你)这是一个非常通用的转换器;不完全确定CanConvert
检查,但对我有用的重要部分是允许typeof(object)
:
/// <summary>
/// To address issues with automatic Int64 deserialization -- see https://stackoverflow.com/a/9444519/1037948
/// </summary>
public class JsonInt32Converter : JsonConverter
{
#region Overrides of JsonConverter
/// <summary>
/// Only want to deserialize
/// </summary>
public override bool CanWrite { get { return false; } }
/// <summary>
/// Placeholder for inheritance -- not called because <see cref="CanWrite"/> returns false
/// </summary>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// since CanWrite returns false, we don't need to implement this
throw new NotImplementedException();
}
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param><param name="objectType">Type of the object.</param><param name="existingValue">The existing value of object being read.</param><param name="serializer">The calling serializer.</param>
/// <returns>
/// The object value.
/// </returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return (reader.TokenType == JsonToken.Integer)
? Convert.ToInt32(reader.Value) // convert to Int32 instead of Int64
: serializer.Deserialize(reader); // default to regular deserialization
}
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Int32) ||
objectType == typeof(Int64) ||
// need this last one in case we "weren't given" the type
// and this will be accounted for by `ReadJson` checking tokentype
objectType == typeof(object)
;
}
#endregion
}
答案 1 :(得分:0)
我遇到了类似的问题,但继续并回答了您的问题 - 如果可能的话,请转到Int32
,然后Int16
,如果可能的话。我也包括测试。对于未来的读者来说,为其他值类型执行此操作也是有意义的,但我只在这里实现了有符号整数。
namespace Serialization
{
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static class JsonSerializer
{
#region Public Methods
public static string Serialize(dynamic obj)
{
return JsonConvert.SerializeObject(obj);
}
public static dynamic Deserialize(string s)
{
var obj = JsonConvert.DeserializeObject(s);
return obj is string ? obj as string : Deserialize((JToken)obj);
}
#endregion
#region Methods
private static dynamic Deserialize(JToken token)
{
// FROM : http://blog.petegoo.com/archive/2009/10/27/using-json.net-to-eval-json-into-a-dynamic-variable-in.aspx
// Ideally in the future Json.Net will support dynamic and this can be eliminated.
if (token is JValue)
{
var value = ((JValue)token).Value;
if (value is Int64)
{
var lValue = (Int64)value;
if (Int32.MinValue <= lValue && lValue <= 0 || 0 < lValue && lValue <= Int32.MaxValue)
{
var iValue = (Int32)lValue;
value = iValue;
// Take out this if you don't want to cast down to Int16.
if (Int16.MinValue <= iValue && iValue <= 0 || 0 < iValue && iValue <= Int16.MaxValue)
{
value = (Int16)iValue;
}
}
}
return value;
}
if (token is JObject)
{
var expando = new ExpandoObject();
(from childToken in token
where childToken is JProperty
select childToken as JProperty).ToList().
ForEach(property => ((IDictionary<string, object>)expando).Add(property.Name, Deserialize(property.Value)));
return expando;
}
if (token is JArray)
{
var items = new List<object>();
foreach (var arrayItem in ((JArray)token)) items.Add(Deserialize(arrayItem));
return items;
}
throw new ArgumentException(string.Format("Unknown token type '{0}'", token.GetType()), "token");
}
#endregion
}
}
namespace Serialization.Tests
{
public class JsonSerializerTests
{
[Test]
public void ShouldDeserializeAsInt16([Values(0, Int16.MaxValue, Int16.MinValue)] Int16 x)
{
var json = string.Format("{{ x: {0} }}", x);
var dynamic = JsonSerializer.Deserialize(json);
Assert.That(dynamic.x.GetType(), Is.EqualTo(typeof(Int16)));
}
[Test]
public void ShouldDeserializeAsInt32([Values(Int16.MaxValue + 1, Int16.MinValue - 1)] Int32 x)
{
var json = string.Format("{{ x: {0} }}", x);
var dynamic = JsonSerializer.Deserialize(json);
Assert.That(dynamic.x.GetType(), Is.EqualTo(typeof(Int32)));
}
[Test]
public void ShouldDeserializeAsInt64([Values(Int32.MaxValue + 1L, Int32.MinValue - 1L)] Int64 x)
{
var json = string.Format("{{ x: {0} }}", x);
var dynamic = JsonSerializer.Deserialize(json);
Assert.That(dynamic.x.GetType(), Is.EqualTo(typeof(Int64)));
}
}
}