C# Newtonsoft.Json.Linq.JValue always returning Int64

后端 未结 2 1324
不知归路
不知归路 2020-12-20 01:13

I am using the Newtonsoft.Json assembly to de-serialize a Json string into a dynamic object (ExpandoObject). The problem I am having is the int value is always returned as a

相关标签:
2条回答
  • 2020-12-20 01:29

    Cross-linking to answer https://stackoverflow.com/a/9444519/1037948

    From How do I change the default Type for Numeric deserialization?

    Paraphrased:

    • The author intentionally chose that all int's come back as Int64 to avoid overflow errors, and it's easier to check (for Json.NET internals, not you)
    • You can get around this with a custom converter like the one posted in the linked answer.

    Here's a really generic converter; not entirely sure about the CanConvert check, but the important part that worked for me was allowing 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
    }
    
    0 讨论(0)
  • 2020-12-20 01:43

    I was having a somewhat similar problem, but went ahead and and answered your question - casting to Int32, if possible and then Int16, if possible. I included tests as well. For future readers, it'd make sense to do this for other value types as well, but I only implemented signed integers here.

    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)));
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题