5

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?

13
  • you can mark the propery with [JsonIgnore] attribute Commented Mar 8, 2016 at 13:29
  • 6
    What do you want to happen? Commented Mar 8, 2016 at 13:29
  • @ZdeslavVojkovic - I may be wrong, but I think OP wants to ignore it because it's in quotes. If so, OP, this surely is a mismatch between the serialized output and the consumer's expectations? Isn't this more of a design consideration? Commented Mar 8, 2016 at 13:29
  • 3
    It's hard to tell what the OP's expectations really are at the moment, as they haven't been put in the question... Commented Mar 8, 2016 at 13:32
  • 2
    "x": "50" <<< "50" is a string, rather than 50 which would be a numeric. OP wants "50" to fail because it's not a numeric. Commented Mar 8, 2016 at 13:35

1 Answer 1

5

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());
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.