web-dev-qa-db-ja.com

ジャクソン:すべての追加コンテンツを拒否するjsonスキーマを生成するにはどうすればよいですか?

"additionalProperties" : falseが私が持っているすべてのクラスに適用されるJSONスキーマを生成したいと思います。

次のクラスがあるとします。

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;
    }
}

以下のコードのようにスキーマを生成しているとき、スキーマプロパティ"additionalProperties" : falseはクラスAにのみ適用されていました。

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

"additionalProperties" : falseがすべてのクラスに適用されるスキーマを生成するにはどうすればよいですか?

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" } } } } }

注:スキームを部分的に生成したくありません。

情報:興味のある人がこの問題の修正をサポートできる場合は、このシナリオの問題を開いています。 すべての追加コンテンツを拒否する必要があるjsonスキーマを生成します

12
Beno Arakelyan

次のように、各プロパティのスキーマを指定する必要があります。

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);

リフレクションAPIを活用して、自動的にそれを行うことができます。これは簡単で汚い例です:

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;
}
3
JEY

以下は私のために働いた:

_import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kjetland.jackson.jsonSchema.JsonSchemaConfig;
import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator;

...

ObjectMapper objectMapper = new ObjectMapper();
JsonSchemaConfig config = JsonSchemaConfig.nullableJsonSchemaDraft4();
JsonSchemaGenerator schemaGenerator = new JsonSchemaGenerator(objectMapper, config);
JsonNode jsonNode = schemaGenerator.generateJsonSchema(Test.class);
String jsonSchemaText = jsonNode.toString();
_

Maven依存関係の使用:

_<dependency>
    <groupId>com.kjetland</groupId>
    <artifactId>mbknor-jackson-jsonschema_2.12</artifactId>
    <version>1.0.28</version>
</dependency>
_

次のクラスを使用します。

Test.Java:

_import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Test {
    @JsonProperty(required = true)
    private final String name;
    private final TestChild child;

    @JsonCreator
    public Test (
            @JsonProperty("name") String name,
            @JsonProperty("child") TestChild child) {
        this.name = name;
        this.child = child;
    }

    public String getName () {
        return name;
    }

    public TestChild getChild () {
        return child;
    }
}
_

...およびTestChild.Java:

_import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class TestChild {
    @JsonProperty(required = true)
    private final String childName;

    @JsonCreator
    public TestChild (@JsonProperty("childName") String childName) {
        this.childName = childName;
    }

    public String getChildName () {
        return childName;
    }
}
_

結果は(きれいなフォーマットのために_jq -C ._を介してパイプされたjsonSchemaTextの出力):

_{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Test",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "name": {
      "type": "string"
    },
    "child": {
      "oneOf": [
        {
          "type": "null",
          "title": "Not included"
        },
        {
          "$ref": "#/definitions/TestChild"
        }
      ]
    }
  },
  "required": [
    "name"
  ],
  "definitions": {
    "TestChild": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "childName": {
          "type": "string"
        }
      },
      "required": [
        "childName"
      ]
    }
  }
}
_

これにより、TestとTestChildの両方で_"additionalProperties": false_になります。

注:スキーマ生成コードでJsonSchemaConfig.nullableJsonSchemaDraft4()JsonSchemaConfig.vanillaJsonSchemaDraft4()に置き換えて、「type:null」または「type:ActualType」の「oneof」参照を削除して、 「type:ActualType」(プロパティに@JsonProperty(required = true)で注釈を付けない限り、これでも「required」配列に追加されないことに注意してください)。

1
Shadow Man

反射を使用したくない場合は、もっと簡単なルートに行きます。 JSONPathを使用します。したがって、以下をpom.xmlに追加する必要があります。

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

次に、以下のコードは、生成されたJSONファイルを変更する方法を示しています

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);
    }
}

実行の出力は(手動でフォーマットされます)

{
  "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
    }
  }
}
0
Tarun Lalwani

これは私の解決策であり、反映やハッキングの方法はなく、私にとっては非常にうまく機能します。

public static void rejectAdditionalProperties(JsonSchema jsonSchema) {
  if (jsonSchema.isObjectSchema()) {
    ObjectSchema objectSchema = jsonSchema.asObjectSchema();
    ObjectSchema.AdditionalProperties additionalProperties = objectSchema.getAdditionalProperties();
    if (additionalProperties instanceof ObjectSchema.SchemaAdditionalProperties) {
        rejectAdditionalProperties(((ObjectSchema.SchemaAdditionalProperties) additionalProperties).getJsonSchema());
    } else {
      for (JsonSchema property : objectSchema.getProperties().values()) {
        rejectAdditionalProperties(property);
      }
      objectSchema.rejectAdditionalProperties();
    }
  } else if (jsonSchema.isArraySchema()) {
    ArraySchema.Items items = jsonSchema.asArraySchema().getItems();
    if (items.isSingleItems()) {
      rejectAdditionalProperties(items.asSingleItems().getSchema());
    } else if (items.isArrayItems()) {
      for (JsonSchema schema : items.asArrayItems().getJsonSchemas()) {
        rejectAdditionalProperties(schema);
      }
    }
  }
}
0
Race