gregsdennis / Manatee.Json

A fully object-oriented approach to JSON manipulation, validation, and serialization that focuses on modeling the JSON structure rather than mere string parsing and conversion.
MIT License
198 stars 32 forks source link

How to validate subschema (like a schema in openapi)? #246

Closed AtosNicoS closed 4 years ago

AtosNicoS commented 4 years ago

Hi! I've written an OpenApi Spec for my Rest API. I want to validate the payload inside the server and the client test suit. The idea was to validate against the existing schemas inside the openapi spec.

But how do I reference a subschema? In My example I would use openapi.json/components/schemas/Test, but the referenced TestId is inside the same file. So I need to read the whole file to parse the schema provided as json path. How can I do this? If this is not possible with this library, does anyone know an alternative (preferred in c#).

[...]
"components": {
    "schemas": {
        "Test": {
            "description": "Test Object",
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "firmwareVersion": {
                    "type": "string",
                    "description": "Version of the Firmware",
                    "example": "1.1.4"
                },
                "id": {
                    "$ref": "#/components/schemas/TestId"
                }
            }
        },
        "TestId": {
            "type": "string",
            "format": "uuid",
            "description": "TestId"
        }
    }
}
gregsdennis commented 4 years ago

I think you're talking about this? That is a fairly large schema, but nothing that Manatee.Json can't handle. I've read in and processed much larger ones.

Yes, you're going to have to parse the entire file before being able to use it. However, once the model is in memory, you can use it.

var text = // content of the file
var json = JsonValue.Parse(text);
var serializer = new JsonSerializer();
var schema = serializer.Deserialize<JsonSchema>(json);

If you hold on to schema in a static field, it'll persist between calls to your Rest API.

There is also a version of Parse() that takes a stream.

Does that answer your question?

AtosNicoS commented 4 years ago

I think that is not a solution for me.

  1. I need to validate against an openapi subschema. I do not want to validate, if my file is a valid openapi schema (the one you linked), I do have a valid openapi schema, that contains other schemas I want to validate. See this example. In this case I want to validate if my json has a property name (from the ref) and a property id.
  2. Openapi schema are parsed a bit different to json schemas. They have a n"nullable" property for example, so that must also be respected.
gregsdennis commented 4 years ago

I think it might be beneficial to come up with a smaller example of what you're trying to do.

It sounds like you have JSON that you want to validate against a subschema of the Open API schema (that I linked). In order to obtain that subschema you need to first read the root schema.

There is a lot of current work to get Open API 3.1 to conform to JSON Schema draft 2019-09 by defining a new vocabulary to define those other keywords. Until then, you can define them yourself and add them to Manatee.Json. See the docs for more info on creating keywords.

AtosNicoS commented 4 years ago

Imagine this schema:

{
  "components": {
    "schemas": {
      "Pet": {
        "allOf": [
          {
            "$ref": "#/components/schemas/NewPet"
          },
          {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "integer",
                "format": "int64"
              }
            }
          }
        ]
      },
      "NewPet": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "tag": {
            "type": "string"
          }
        }
      }
    }
  }
}

And this data:

{
"name": "TestPet",
"tag": "cat",
"id": 32
}

And this invalid data

{
"nickname": "TestPet2",
"tag": "cat",
"id": 64
}

Ecept there are some openapi specific properties that will get resolved in 3.1 (as you explained), how would I validate this json with the provided schema? I'd need to validate against the schema path components/schemas/Pet.

gregsdennis commented 4 years ago

Okay. I think I see your problem. Unfortunately, I don't think it's one that any library is going to be able to solve without some work on your end.

The main issue is that components is not a recognized JSON Schema keyword (in any draft), so the spec says to skip it.

Additionally, if you isolate the Pet subschema and validate on just that, the subschema becomes the root, and it won't be able to resolve the #/components/schemas/NewPet reference because that requires the resolution logic to navigate outside of the root schema. I think this problem is one that @handrews was working so hard to resolve by creating the $recursiveRef and $recursiveAnchor keywords for draft 2019-09.

The only solution I can think of is storing the Pet and NewPet schemas in their own files and giving them unique $ids. Then you can reference them directly. (You'll need to register them with JsonSchemaRegistry before you attempt to validate. See the docs for more info on registration.)