Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I change the default Type for Numeric deserialization?

Tags:

c#

json.net

I'm deserializing some properties to a Dictionary<string, object>.

When I deserialize some json, it populates the Dictionary with Int64 objects rather than Int32. I would like it to choose Int32 as the default well knowing that I could have javascript Numerics that would overflow on conversion. Throwing an exception in that case would be entirely acceptable.

Is there any way to achieve that? I'm hoping for some nice attributes or a convenient interface that could be implemented and added to the JsonSerializer. And I fear that I have to go deep down into the depths of Json.NET.

Basically I would like to have some way to control the known types for the objects so that I could get Int32's instead of Int64 and DateTimes instead of Strings.

like image 600
Gaute Løken Avatar asked Nov 28 '11 14:11

Gaute Løken


People also ask

How do I deserialize text JSON?

A common way to deserialize JSON is to first create a class with properties and fields that represent one or more of the JSON properties. Then, to deserialize from a string or a file, call the JsonSerializer. Deserialize method.

What is JsonSerializer deserialize?

Deserialize<TValue>(JsonNode, JsonSerializerOptions) Converts the JsonNode representing a single JSON value into a TValue . Deserialize<TValue>(JsonElement, JsonTypeInfo<TValue>) Converts the JsonElement representing a single JSON value into a TValue . Deserialize<TValue>(JsonElement, JsonSerializerOptions)

Is polymorphic Deserialization possible in system text JSON?

There is no polymorphic deserialization (equivalent to Newtonsoft. Json's TypeNameHandling ) support built-in to System.


1 Answers

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 
like image 146
enzi Avatar answered Sep 22 '22 04:09

enzi