GKalnytskyi
GKalnytskyi

Reputation: 629

JsonSchema.Net removing false-positive EvaluationResults

I am using JsonSchema.Net (docs) to validate Json document against a prepared schema. Some of the properties are defined in the schema to be nullable (as described in the documentation {"oneOf":[{"type":"string"},{"type":"null"}]})

When I check a document against the schema some evaluation details for nullable properties will be returned as IsValid = false with errors opposite of what is in the Json (e.g. "Value is "null" but should be "integer"", or "Value is "number" but should be "null"").

I would like to understand what I might be doing wrong in this case. And how may I filter such false-positives, so that if Json document is invalid, I could quickly find nodes that are the cause, and what is wrong with them.

Document Example:

{
  "referenceNumber": "35366",
  "storageZone": null,
  "maxCount": null,
  "length":  124.5
}

Document's Json Schema:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Schema Example",
  "$defs": {
    "stringNullable": {
      "oneOf": [
        { "type": "string" },
        { "type": "null" }
      ]
    },
    "integerNullable": {
      "oneOf": [
        { "type": "integer" },
        { "type": "null" }
      ]
    },
    "numberNullable": {
      "oneOf": [
        { "type": "number" },
        { "type": "null" }
      ]
    }
  },
  "type": "object",
  "properties": {
    "referenceNumber": {
      "type": "string"
    },
    "storageZone": {
      "$ref": "#/$defs/stringNullable"
    },
    "maxCount": {
      "$ref": "#/$defs/integerNullable"
    },
    "length": {
      "$ref": "#/$defs/numberNullable"
    }
  }
}

Code Example:

[Test]
public void JsonSchemaAsserts()
{
    var schema = JsonSchema.FromFile("./SchemaExample.json");
    var jsonText = File.ReadAllText("./DataExample.json");
    var json = JsonNode.Parse(jsonText);

    var validationResult = schema.Evaluate(json, new EvaluationOptions() { OutputFormat = OutputFormat.List });

    Assert.That(validationResult.IsValid, Is.True);

    var validationErrors = validationResult.Details.Where(d => !d.IsValid && d.HasErrors).ToList();

    Assert.That(validationResult, Is.Empty);
}

Upvotes: 1

Views: 401

Answers (1)

Jeremy Fiel
Jeremy Fiel

Reputation: 3297

Because you are using oneOf, there is always going to be a false schema when more than one schema is validated.

If you want a cleaner way to write the same schema, you can use the array form of type.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Schema Example",
  "$defs": {
    "stringNullable": {
      "type": [
        "string",
        "null"
      ]
    },
    "integerNullable": {
      "type": [
        "integer",
        "null"
      ]
    },
    "numberNullable": {
      "type": [
        "number",
        "null"
      ]
    }
  },
  "type": "object",
  "properties": {
    "referenceNumber": {
      "type": "string"
    },
    "storageZone": {
      "$ref": "#/$defs/stringNullable"
    },
    "maxCount": {
      "$ref": "#/$defs/integerNullable"
    },
    "length": {
      "$ref": "#/$defs/numberNullable"
    }
  }
}

This will give the following results

{
  "valid": true,
  "evaluationPath": "",
  "schemaLocation": "https://json-everything.net/2f7acd351a#",
  "instanceLocation": "",
  "annotations": {
    "title": "Schema Example",
    "properties": [
      "referenceNumber",
      "storageZone",
      "maxCount",
      "length"
    ]
  },
  "details": [
    {
      "valid": true,
      "evaluationPath": "/properties/referenceNumber",
      "schemaLocation": "https://json-everything.net/2f7acd351a#/properties/referenceNumber",
      "instanceLocation": "/referenceNumber"
    },
    {
      "valid": true,
      "evaluationPath": "/properties/storageZone",
      "schemaLocation": "https://json-everything.net/2f7acd351a#/properties/storageZone",
      "instanceLocation": "/storageZone",
      "details": [
        {
          "valid": true,
          "evaluationPath": "/properties/storageZone/$ref",
          "schemaLocation": "https://json-everything.net/2f7acd351a#/$defs/stringNullable",
          "instanceLocation": "/storageZone"
        }
      ]
    },
    {
      "valid": true,
      "evaluationPath": "/properties/maxCount",
      "schemaLocation": "https://json-everything.net/2f7acd351a#/properties/maxCount",
      "instanceLocation": "/maxCount",
      "details": [
        {
          "valid": true,
          "evaluationPath": "/properties/maxCount/$ref",
          "schemaLocation": "https://json-everything.net/2f7acd351a#/$defs/integerNullable",
          "instanceLocation": "/maxCount"
        }
      ]
    },
    {
      "valid": true,
      "evaluationPath": "/properties/length",
      "schemaLocation": "https://json-everything.net/2f7acd351a#/properties/length",
      "instanceLocation": "/length",
      "details": [
        {
          "valid": true,
          "evaluationPath": "/properties/length/$ref",
          "schemaLocation": "https://json-everything.net/2f7acd351a#/$defs/numberNullable",
          "instanceLocation": "/length"
        }
      ]
    }
  ]
}

Upvotes: 1

Related Questions