C#Newtonsoft.Json.Linq.JValue总是返回Int64

我使用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)expando).Add(property.Name, Deserialize(property.Value))); return expando; } if (token is JArray) { var items = new List(); 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在reflection中被用于某些类型检查,并且它失败了。 任何想法为什么会发生这种情况将不胜感激。

交叉链接以回答https://stackoverflow.com/a/9444519/1037948

从如何更改数字反序列化的默认类型?

转述:

  • 作者故意选择所有int作为Int64返回以避免溢出错误,并且更容易检查(对于Json.NET内部,而不是你)
  • 您可以使用自定义转换器(如链接答案中发布的转换器)解决此问题。

这是一个非常通用的转换器; 不完全确定CanConvert检查,但对我CanConvert的重要部分是允许typeof(object)

 ///  /// To address issues with automatic Int64 deserialization -- see https://stackoverflow.com/a/9444519/1037948 ///  public class JsonInt32Converter : JsonConverter { #region Overrides of JsonConverter ///  /// Only want to deserialize ///  public override bool CanWrite { get { return false; } } ///  /// Placeholder for inheritance -- not called because  returns false ///  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // since CanWrite returns false, we don't need to implement this throw new NotImplementedException(); } ///  /// Reads the JSON representation of the object. ///  /// The  to read from.Type of the object.The existing value of object being read.The calling serializer. ///  /// The object value. ///  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 } ///  /// Determines whether this instance can convert the specified object type. ///  /// Type of the object. ///  /// true if this instance can convert the specified object type; otherwise, false. ///  public override bool CanConvert(Type objectType) { return objectType == typeof(Int32) || objectType == typeof(Int64) || objectType == typeof(int) || // 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 } 

我有一个类似的问题,但继续并回答你的问题 – 如果可能的话,转向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)expando).Add(property.Name, Deserialize(property.Value))); return expando; } if (token is JArray) { var items = new List(); 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))); } } }