Java Jackson:如何生成拒绝所有附加内容的json模式

Java Jackson:如何生成拒绝所有附加内容的json模式,java,json,jackson,jsonschema,json-schema-validator,Java,Json,Jackson,Jsonschema,Json Schema Validator,我想生成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;

我想生成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
的模式

模式的示例

{
“类型”:“对象”,
“id”:“urn:jsonschema:com.xxx.xxx:A”,
“附加属性”:false,
“财产”:{
“s”:{
“类型”:“字符串”
},
“b”:{
“类型”:“对象”,
“id”:“urn:jsonschema:com.xxx.xxx:B”,
“财产”:{
“bd”:{
“类型”:“编号”
}
}
}
}
}

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

有关信息:
我已经为这个场景打开了一个问题,如果您感兴趣的人可以支持这个问题的修复

您需要为每个属性指定模式,如:

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;
}
publicstaticvoidmain(字符串[]args)抛出JsonProcessingException{
最终ObjectMapper映射器=新ObjectMapper();
最终JsonSchemaGenerator schemaGen=新JsonSchemaGenerator(映射器);
ObjectSchema=generateSchema(schemaGen,A.class);
schema.rejectAdditionalProperties();
System.out.print(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema));
}
公共静态ObjectSchema generateSchema(JsonSchemaGenerator生成器,类类型)引发JsonMappingException{
ObjectSchema=generator.generateSchema(类型).asObjectSchema();
for(最后一个字段:type.getDeclaredFields()){
如果(!field.getType().getName().startsWith(“java”)和&!field.getType().isPrimitive()){
final ObjectSchema fieldSchema=generateSchema(生成器,field.getType());
fieldSchema.rejectAdditionalProperties();
schema.putProperty(field.getName(),fieldSchema);
}
}
返回模式;
}

如果您不想使用反射,我会选择更简单的路线。我会使用JSONPath。因此,您需要将以下内容添加到
pom.xml

  <dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.3.0</version>
  </dependency>
运行的输出为(手动格式化)


以下几点对我很有用:

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>
…和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
的输出,用于精确格式化):

这会导致Test和TestChild上出现“additionalProperties”:false


注意:您可以在模式生成代码中将
JsonSchemaConfig.nullableJsonSchemaDraft4()
替换为
JsonSchemaConfig.vanillaJsonSchemaDraft4()
,以摆脱带有“type:null”或“type:ActualType”的“其中一个”引用,而只使用“type:ActualType”(注意,这仍然不会将它们添加到“required”中)数组,除非您使用
@JsonProperty(required=true)
)注释属性。

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

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

谢谢你的回答,但我不想手动放置属性。Jackson有任何api来提供吗?在我的回答中,我建议使用java反射api来自动完成。我不知道还有什么其他方法可以做到这一点。请注意,这也会导致基于“引用”的模式,而不是问题中提供的示例中所示的扁平模式(不允许自引用结构,例如分支可能包含分支和/或叶子的树)。谢谢。我将尝试您的解决方案并让您知道。它是scala库。有提供相同功能的
java
库吗?我使用的是这个,而不是scala。我使用的是纯java。这个库可能是用scala编写的,但它肯定是用java编写的。就我个人而言,我不认为这是在某个地方的服务中使用的东西。我认为这是在maven插件中运行的东西。在我的工作中,我们编写了这样一个插件,作为maven构建过程的一部分,从我们的模型类生成模式,并将其上传到S3存储桶,因此我们甚至不将这些LIB拉入生成的JAR。
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;
    }
}
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;
    }
}
{
  "$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"
      ]
    }
  }
}
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);
      }
    }
  }
}