The JSON looks like this:
{
"x": "50"
}
And the class looks like this:
public class Test
{
public float? x { get; set; }
}
And when using
var test = JsonConvert.DeserializeObject<Test>(json);
No exception is thrown, JSON.NET just converts the string value "50" into a float value 50.0.
This question is raised in the the context of input-validation. I want to get an exception, because the JSON string does not comply to the contract (the x
field should be a real float).
And I don't want to use property annotations in the 'Test' class.
Is there a JsonSerializerSettings which can be be used to avoid this?
JSON.NET liberally parses numbers-in-strings ("50"
) as numbers. There's no trivial way to turn this off, as far as I can find.
You could create a custom converter that disallows 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 to be able to deserialize into specified types, in this case float
and float?
, but configurable.
Upon deserialization, it inspects the token type. Some token types for given JSON input:
"50"
: JTokenType.String
50
: JTokenType.Integer
42.1415
: JTokenType.Float
This way the converter can determine whether the current token is formatted as desired. When the token type is a string, the above converter will throw an exception stating it won't convert a string to the desired type.
When the token type is anything else, the converter will convert the token to the appropriate numeric type through token.ToObject(objectType)
. That method will also handle non-numeric input by throwing the appropriate exception, for example "Can not convert Array to Single.".
Given a class Foo
:
public class Foo
{
public float Bar { get; set; }
public string Baz { get; set; }
public float? Qux { get; set; }
}
Deserialize a JSON string using 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 throw:
var jsonString = "{ \"Bar\" : 50, \"Baz\" : \"zaB\", \"Qux\" : \"42.1415\" }";
var foo2 = JsonConvert.DeserializeObject<Foo>(jsonString, new NumberConverter());
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