Avoid deserializing the JSON string character for a numeric property

JSON is as follows:

{ "x": "50" } 

And the class is as follows:

 public class Test { public float? x { get; set; } } 

And when using

 var test = JsonConvert.DeserializeObject<Test>(json); 

No exception is thrown; JSON.NET simply converts the string value "50" to a floating point value of 50.0.

This question arises in the context of input validation. I want to get an exception because the JSON string does not match the contract (the x field must be a real float).

And I do not want to use property annotations in the Test class.

Are there any JsonSerializerSettings that can be used to avoid this?

+5
source share
1 answer

JSON.NET freely parses numbers in strings ( "50" ) as numbers. There is no trivial way to disable this as far as I can find.

You can create a custom converter that prohibits this:

 public class NumberConverter : JsonConverter { private readonly Type[] _typesNotToReadAsString = { typeof(float), typeof(float?) }; public override bool CanConvert(Type objectType) { return _typesNotToReadAsString.Any(t => t.IsAssignableFrom(objectType)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JToken token = JToken.Load(reader); if (_typesNotToReadAsString.Contains(objectType) && token.Type == JTokenType.String) { string exceptionString = string.Format("Won't convert string to type {0}", objectType.FullName); throw new JsonSerializationException(exceptionString); } return token.ToObject(objectType); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } public override bool CanWrite { get { return false; } } } 

The converter reports that it can be deserialized to the specified types, in this case float and float? but customizable.

Upon deserialization, it checks the type of token. Some types of tokens for a given JSON input:

  • "50" : JTokenType.String
  • 50 : JTokenType.Integer
  • 42.1415 : JTokenType.Float

In this way, the converter can determine if the current token will be formatted as desired. When the token type is a string, the converter above will throw an exception, indicating that it does not convert the string to the desired type.

When the token type is something else, the converter will convert the token to the corresponding numeric type via token.ToObject(objectType) . This method also handles non-numeric input, throwing an appropriate exception, for example, "Cannot convert array to single."

For the Foo class:

 public class Foo { public float Bar { get; set; } public string Baz { get; set; } public float? Qux { get; set; } } 

deserialize the JSON string using the above converter, this will work:

 var jsonString = "{ \"Bar\" : 50, \"Baz\" : \"zaB\", \"Qux\" : 42.1415 }"; var foo = JsonConvert.DeserializeObject<Foo>(jsonString, new NumberConverter()); 

While this will be issued:

 var jsonString = "{ \"Bar\" : 50, \"Baz\" : \"zaB\", \"Qux\" : \"42.1415\" }"; var foo2 = JsonConvert.DeserializeObject<Foo>(jsonString, new NumberConverter()); 
+5
source

Source: https://habr.com/ru/post/1244659/


All Articles