【问题标题】:Jackson: How can I generate json schema which rejects all additional content杰克逊:如何生成拒绝所有附加内容的 json 模式
【发布时间】:2018-08-05 15:48:49
【问题描述】:

我想生成 JSON 架构,其中 "additionalProperties" : false 将应用于我拥有的所有类。

假设我有以下课程:

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

注意:我不想部分地生成方案。

有关信息: 如果有人感兴趣,您可以支持修复此问题,我已经为这种情况打开了问题。 Generate json schema which should rejects all additional content

【问题讨论】:

    标签: java json jackson jsonschema json-schema-validator


    【解决方案1】:

    您需要为每个属性指定架构,例如:

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

    【讨论】:

    • 感谢您的回答,但我不想手动放置属性。 Jackson 是否有任何 api 可以提供该功能?
    • 在我的回答中,我建议使用 java 反射 API 来自动完成。我不知道有其他方法可以做到这一点。
    【解决方案2】:

    如果您不想使用反射,我会采用更简单的方法。我会使用 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
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      以下内容对我有用:

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

      结果(jsonSchemaText 的输出通过jq -C . 传输以进行漂亮的格式化):

      {
        "$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) 注释属性,否则这仍然不会将它们添加到“必需”数组中)。

      【讨论】:

      • 请注意,这也会导致基于“引用”的模式,而不是扁平模式(不允许自引用结构,例如分支可能包含分支和/或叶子的树),例如显示在问题中提供的示例中。
      • 谢谢。我会尝试您的解决方案并通知您。
      • 是scala库。是否有提供相同功能的java 库?
      • 我正在使用它,但我根本没有使用 scala。我正在使用纯Java。该库可能是用 scala 编写的,但它肯定可以在 java 中运行。就个人而言,我不认为这是在某处服务中使用的东西。我认为这是在 Maven 插件中运行的东西。我们在我的工作中编写了这样一个插件来从我们的模型类生成模式作为 maven 构建过程的一部分并将其上传到 S3 存储桶,因此我们甚至不会将这些库拉入我们生成的 jar 中。
      【解决方案4】:

      这是我的解决方案,没有任何反射和破解方式,对我来说效果很好。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-02
        • 1970-01-01
        • 2013-08-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多