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
.
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.
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)
There is no polymorphic deserialization (equivalent to Newtonsoft. Json's TypeNameHandling ) support built-in to System.
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 Int64
s 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With