How do I change the default Type for Numeric deserialization?

后端 未结 4 1702
抹茶落季
抹茶落季 2020-11-30 05:10

I\'m deserializing some properties to a Dictionary.

When I deserialize some json, it populates the Dictionary with <

相关标签:
4条回答
  • 2020-11-30 05:30

    I'm accepting Enzi's answer since it's what I was asking for.

    However, since then I've changed my strategy.

    Right now I'm deserializing to a ChangeSet<T> which instead of the dictionary has a strongly typed Entity (T) object with the changes. It also has a List<string> with the property names of the properties that was present in the incoming json. I then populate that list during deserialization using a custom MediaFormatter. That way I get a strongly typed object and correct deserialization of all properties, and I know from the list what properties I should set on my collection of T when I want to do my batch operation.

    This way I basically use my entities as DTO's without having to have a myriad of different DTO's for different batch operations. Is pretty sleek if I do say so myself. :)

    0 讨论(0)
  • 2020-11-30 05:36

    As far as I know, there is no built-in way to do that.

    There was an issue on this subject, but it has been closed. Some comments from the author on the issue:

    Json.NET by default reads integer values as Int64 because there is no way to know whether the value should be Int32 or Int64, and Int64 is less likely to overflow. For a typed property the deserializer knows to convert the Int64 to a Int32 but because your property is untyped you are getting an Int64. [...] It is just the way Json.NET has to work.

    The easiest solution would of coure be to change the type to Dictionary<string, int>, but I suppose you are not only reading numerics and thus are stuck with object.

    Another option would be to either use Serialization Callbacks and manually convert those Int64s to Int32 or create your own Contract Resolver JsonConverter and directly control the (de-)serialization.


    Edit: I created a little example to be more specific.

    Here is a very basic converter that only works with your specifc Dictionary:

    public class Int32Converter : JsonConverter {
        public override bool CanConvert(Type objectType) {
            // may want to be less concrete here
            return objectType == typeof(Dictionary<string, object>);
        }
    
        public override bool CanWrite {
            // we only want to read (de-serialize)
            get { return false; }
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
            // again, very concrete
            Dictionary<string, object> result = new Dictionary<string, object>();
            reader.Read();
    
            while (reader.TokenType == JsonToken.PropertyName) {
                string propertyName = reader.Value as string;
                reader.Read();
    
                object value;
                if (reader.TokenType == JsonToken.Integer)
                    value = Convert.ToInt32(reader.Value);      // convert to Int32 instead of Int64
                else
                    value = serializer.Deserialize(reader);     // let the serializer handle all other cases
                result.Add(propertyName, value);
                reader.Read();
            }
    
            return result;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
            // since CanWrite returns false, we don't need to implement this
            throw new NotImplementedException();
        }
    }
    

    You can either use attributes to decorate members with your converter or pass it as parameter to a (de-)serialize method. Here's an example where I used an attribute:

        [JsonObject]
    public class MyObject {
        [JsonConverter(typeof(Int32Converter))]
        public Dictionary<string, object> Properties { get; set; }
    }
    

    And here's the code I used to test the implementation:

    class Program {
        static void Main(string[] args) {
            MyObject test = new MyObject();
            test.Properties = new Dictionary<string, object>() { { "int", 15 }, { "string", "hi" }, { "number", 7 } };
            Print("Original:", test);
    
            string json = JsonConvert.SerializeObject(test);
            Console.WriteLine("JSON:\n{0}\n", json);
    
            MyObject parsed = JsonConvert.DeserializeObject<MyObject>(json);
            Print("Deserialized:", parsed);
        }
    
        private static void Print(string heading, MyObject obj) {
            Console.WriteLine(heading);
            foreach (var kvp in obj.Properties)
                Console.WriteLine("{0} = {1} of {2}", kvp.Key, kvp.Value, kvp.Value.GetType().Name);
            Console.WriteLine();
        }
    }
    

    Without the converter, the result would be:

    Deserialized:
    int = 15 of Int64
    string = hi of String
    number = 7 of Int64
    

    and with the converter it is:

    Deserialized:
    int = 15 of Int32
    string = hi of String
    number = 7 of Int32
    
    0 讨论(0)
  • 2020-11-30 05:37

    This is working well for me:

    public class ParseNumbersAsInt32Converter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(long) || objectType == typeof(long?) || objectType == typeof(object);
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.Value != null && reader.Value is long)
            {
                return Convert.ToInt32(reader.Value);
            }
            return reader.Value;
        }
    }
    
    0 讨论(0)
  • Try

       var variable = Convert.ToInt32(object) 
    

    Iterate the Dictionary<string,object> once and rewrite its object with this Int32, or do the Int32 conversion each time you read the object.

    0 讨论(0)
提交回复
热议问题