Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson: How can I generate json schema which rejects all additional content

I want to generate JSON schema where "additionalProperties" : false will be applied for all classes which I have.

Suppose I have following classes:

class A{
    private String s;
    private B b;

    public String getS() {
        return s;
    }

    public B getB() {
        return b;
    }
}

class B{
    private BigDecimal bd;

    public BigDecimal getBd() {
        return bd;
    }
}

When I am generating schema as following like below code the schema property "additionalProperties" : false was applying only for the class A.

ObjectMapper mapper = new ObjectMapper();
JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper);
ObjectSchema schema = schemaGen.generateSchema(A.class).asObjectSchema();
schema.rejectAdditionalProperties();
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema);

How can I generate the schema where "additionalProperties" : false will be applied on all classes?

Example of schema

{ "type" : "object", "id" : "urn:jsonschema:com.xxx.xxx:A", "additionalProperties" : false, "properties" : { "s" : { "type" : "string" }, "b" : { "type" : "object", "id" : "urn:jsonschema:com.xxx.xxx:B", "properties" : { "bd" : { "type" : "number" } } } } }

Note: I don't want to generate schemes part by part.

For info: I have opened issue for this scenario if someone interested you can support fix of this issue. Generate json schema which should rejects all additional content

like image 602
Beno Arakelyan Avatar asked Feb 26 '18 08:02

Beno Arakelyan


2 Answers

You will need to specify the schema for each properties like:

ObjectMapper mapper = new ObjectMapper();
JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper);
ObjectSchema schemaB = schemaGen.generateSchema(B.class).asObjectSchema();
schemaB.rejectAdditionalProperties();

ObjectSchema schema = schemaGen.generateSchema(A.class).asObjectSchema();
schema.rejectAdditionalProperties();
schema.putProperty("b", schemaB);

You can leverage reflection api to automatically do it for you. Here is a quick and dirty example:

public static void main(String[] args) throws JsonProcessingException {
    final ObjectMapper mapper = new ObjectMapper();
    final JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper);
    ObjectSchema schema = generateSchema(schemaGen, A.class);
    schema.rejectAdditionalProperties();
    System.out.print(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema));
}

public static <T> ObjectSchema generateSchema(JsonSchemaGenerator generator, Class<T> type) throws JsonMappingException {
    ObjectSchema schema = generator.generateSchema(type).asObjectSchema();
    for (final Field field : type.getDeclaredFields()) {
        if (!field.getType().getName().startsWith("java") && !field.getType().isPrimitive()) {
            final ObjectSchema fieldSchema = generateSchema(generator, field.getType());
            fieldSchema.rejectAdditionalProperties();
            schema.putProperty(field.getName(), fieldSchema);
        }
    }
    return schema;
}
like image 130
JEY Avatar answered Oct 15 '22 23:10

JEY


Well I would go to a simpler route if you don't want to use reflections. I would use JSONPath. So you would need to add below to your pom.xml

  <dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.3.0</version>
  </dependency>

Then below code demonstrates how to alter the generated JSON file

package taruntest;

import com.jayway.jsonpath.*;

public class Test {

    public static void main(String[] args) throws Exception {
        String data = "{\n" +
                "  \"type\" : \"object\",\n" +
                "  \"id\" : \"urn:jsonschema:com.xxx.xxx:A\",\n" +
                "  \"additionalProperties\" : false,\n" +
                "  \"properties\" : {\n" +
                "    \"s\" : {\n" +
                "      \"type\" : \"string\"\n" +
                "    },\n" +
                "    \"b\" : {\n" +
                "      \"type\" : \"object\",\n" +
                "      \"id\" : \"urn:jsonschema:com.xxx.xxx:B\",\n" +
                "      \"properties\" : {\n" +
                "        \"bd\" : {\n" +
                "          \"type\" : \"number\"\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "}";

        DocumentContext doc = JsonPath.parse(data);
        doc.put("$..[?(@.id =~ /urn:jsonschema:.*/)]", "additionalProperties", false);
        String modified =  doc.jsonString();
        System.out.println(modified);
    }
}

The output of the run is (formatted manually)

{
  "type": "object",
  "id": "urn:jsonschema:com.xxx.xxx:A",
  "additionalProperties": false,
  "properties": {
    "s": {
      "type": "string"
    },
    "b": {
      "type": "object",
      "id": "urn:jsonschema:com.xxx.xxx:B",
      "properties": {
        "bd": {
          "type": "number"
        }
      },
      "additionalProperties": false
    }
  }
}
like image 29
Tarun Lalwani Avatar answered Oct 15 '22 23:10

Tarun Lalwani