2

I need to validate JSON response dynamically. Schema should depends on value of specific key in key-pair.

Please, note, that I am using JSON.NET 5.0.8 and cannot upgrade to higher version due to compatibility with infrastructre.

If value of "type" is not "SumProperty" ("CountersProperty" in example), then validate "rule" like string:

"property": [
    {
        "type": "CountersProperty",
        "rule": "MEQUAL"
    }
]

But! If value of "type" is "SumProperty", then validate "rule" like array (and "rule" should be inside "config"):

  "property": [
      {
          "type": "SumProperty",
          "config": {
              "rule": [
                  {
                      "type": "MEQUAL",
                      "value": 2
                  }
              ]
          }
      }
 ]

So I need some kind of dynamic validation, that can "understand" what kind of property we have and validate it appropriately. JSON response can have multiple of "properties" at the same time, so I can't choose one kind of validation or another, it should work dynamically.

Nick
  • 33
  • 7

2 Answers2

4

You can do this by implementing a custom JsonConverter.

I made the following classes following your sample input

public class Schema
{
    [JsonProperty("Property")]
    public List<Property> Properties { get; set; }
}

public abstract class Property
{
    public string Type { get; set; }
}

public class NotSumProperty : Property
{
    public string Rule { get; set; }
}

public class SumProperty : Property
{
    public Config Config { get; set; }
}

public class Config
{
    [JsonProperty("Rule")]
    public List<Rule> Rules { get; set; }
}

public class Rule
{
    public string Type { get; set; }
    public int Value { get; set; }
}

Then we define our custom JsonConverter. We override the ReadJson() method to implement our conversion clause, in this case, we evaluate the type of the Property.

public class PropertyConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => typeof(Property).IsAssignableFrom(objectType);

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = JObject.Load(reader);
        Property p;

        switch ((string)obj["type"])
        {
            case "SumProperty":
                p = new SumProperty();
                break;
            default:
                p = new NotSumProperty();
                break;
        }

        serializer.Populate(obj.CreateReader(), p);
        return p;
    }
}

Finally, here's the usage:

JsonSerializerSettings settings = new JsonSerializerSettings 
{ 
    TypeNameHandling = TypeNameHandling.Objects 
};
settings.Converters.Add(new PropertyConverter());

Schema schema = JsonConvert.DeserializeObject<Schema>(json, settings);
Innat3
  • 3,561
  • 2
  • 11
  • 29
  • Thanks! I think I'll use this pattern to solve the issue. My JSON is huge and so as schema, so a little more code for validation is not a criminal =) – Nick Oct 15 '19 at 09:08
0

Another option, if you don't want to write your own converter, is to deserialize into a a dynamic object, and check these values at run time, as demonstrated here.

This could potentially be more useful if you can't define a clear inheritance patter, though it does rely on clients to implement more parsing/validation logic themselves. In other words, it's a little easier to hit unexpected exceptions - it basically moves potential issues from compile-time errors to run-time errors.

chill94
  • 341
  • 1
  • 5
  • My validation schema is complex, so it will become even more complicated with this. But thank you anyway! – Nick Oct 15 '19 at 09:10