jsonschema-generator icon indicating copy to clipboard operation
jsonschema-generator copied to clipboard

Validate Json error

Open cbuchmann opened this issue 4 years ago • 9 comments

Hi,

I have the following class with the @JsonTypeInfo annotation.

@JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "type")
public class MyEvent {

  @JsonProperty(required = true)
  private final String name;

  public MyEvent(String name) {
    this.name = name;
  }
}

When I generate the schema (with the Jackson-module) for this class I get the following result with two sub-schemas.

{
  "$schema" : "http://json-schema.org/draft-07/schema#",
  "allOf" : [ {
    "type" : "object",
    "properties" : {
      "name" : { }
    },
    "required" : [ "name" ],
    "additionalProperties" : false
  }, {
    "type" : "object",
    "properties" : {
      "type" : {
        "const" : "com.event.MyEvent"
      }
    }
  } ]
}

When I try to validate the following Json against the schema above

{
  "name": "Test",
  "type": "com.event.MyEvent"
}

using the following code:

@Test
public void testMyEvent() throws JsonProcessingException {
    JSONObject jsonSchema = new JSONObject(
        new JSONTokener(SchemaGeneratorApp.class.getResourceAsStream("/schemas/com.event.MyEvent.json")));

    JSONObject jsonSubject = new JSONObject(
        new JSONTokener(SchemaGeneratorApp.class.getResourceAsStream("/data/MyEvent.json")));

    JsonSchema schema = validatorFactory.getSchema(jsonSchema.toString());
    schema
        .validate(mapper.readTree(jsonSubject.toString()))
        .forEach(System.out::println);
}

I get the following error:

$.type: is not defined in the schema and the schema does not allow additional properties

How can I resolve this error in the simplest way?

cbuchmann avatar May 28 '21 14:05 cbuchmann

Hi @cbuchmann,

The starting point would be to determine what configuration is producing this line in the first subschema:

"additionalProperties" : false

That is blocking any additional properties defined in the second subschema, which are not already mentioned in the first.


In other words:

  • The validation result is correct. The first subschema (in the "allOf") says: there is exactly one property called "name" and nothing else. The second subschema says, there may be "type" property and if that is present, it has a particular value. The combination of those two schemas would only allow for a JSON like this one:
{
    "name": "Test"
}
  • As stated above, the culprit is the "additionalProperties": false. That is not being added by default. I suspect you have explicitly included it – one way or another (e.g. by including the Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT, which is not compatible with your currently selected subtype resolution. You'd have to remove this Option or implement the type resolution yourself in some different way).
  • I can only help more specifically if you share your generator configuration. 😉

CarstenWickner avatar May 28 '21 16:05 CarstenWickner

Thanks for your quick response. I removed the option FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT in the jackson-module and then the validation works. But then if I add a property in the Json data that should not be there it will also pass.

{
  "name": "Test",
  "type": "com.event.MyEvent",
  "abcd": "efgh"
}

Is there another way to prevent that? I guess what I would like is for the two subschemas to be merged into one so that additionalProperties can be set to false. Is there a way to make the second sub-schema that is produced by the annotation @JsonTypeInfo to be incorporated in the first.

cbuchmann avatar May 28 '21 19:05 cbuchmann

Theoretically, that should be possible I guess. I can have a look. Generally speaking, I'm also in favor of pull requests. 😃

CarstenWickner avatar May 28 '21 19:05 CarstenWickner

Now I remember why I did it like this. It avoids an endless loop that results in a StackOverflowError. Have to think about it again, but can't promise anything at this point.

CarstenWickner avatar May 29 '21 05:05 CarstenWickner

Thanks, I appreciate your effort. Instead of changing the schema Is it possible to change anything in the validation process to make this work?

cbuchmann avatar May 29 '21 11:05 cbuchmann

I'm afraid not. The validation is working as it should.

The first subschema forbids the "type" property. You could either remove the"additionalProperties": false or already declare the "type" property in the first subschema.

CarstenWickner avatar May 29 '21 12:05 CarstenWickner

Thanks for your suggestions. One other option would be to do some post processing on the generated schema node outside of the framework and manually merge the two sub-schemas.

cbuchmann avatar May 30 '21 05:05 cbuchmann

Yes. There is already some post-processing going on (e.g. to merge unnecessary allOf parts), which could probably be enhanced somewhat. However, in this particular case, merging the two subschemas alters their meaning. You know that you want this different meaning and can therefore do it in your particular use case, but that prevents a generic solution.

I haven't given up though. I'm still looking into avoiding the endless loops (which would already be a good thing on its own 😅 ), which would, in turn, allow for the Jackson subtype handling to never even produce the allOf parts that you want to get rid off.

CarstenWickner avatar May 30 '21 07:05 CarstenWickner

I think this is a pretty common issue with JSON schemas. https://stackoverflow.com/a/23001194/5153063 Solutions seem to have all sorts of ugly duplication involved. Maybe this is why the ajv library seems to be pushing people towards JTD?

rfox12 avatar Sep 14 '21 00:09 rfox12

While I haven't found a way to avoid the allOf parts being added here without messing up the generated schema or running into endless loops, I'm happy to say that the built-in clean-up logic for the allOf blocks has been improved (as long as there is no "additionalProperties": false in there) with the result of the ´allOf` being removed during the postprocessing (in most/simple scenarios).

This will be available with the next release 4.28.0.

I'm closing this issue accordingly, as I currently don't see a way of further enhancing the described use-case. Always happy to receive pull requests though. 😉

CarstenWickner avatar Oct 22 '22 19:10 CarstenWickner