jackson Java:将带有$ref的复合JSON模式反序列化为一个模式

58wvjzkj  于 2022-11-09  发布在  Java
关注(0)|答案(2)|浏览(71)

根据Structuring a complex schema,有可能有如下关系:
1.基本JSON模式(customer.json)

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "billing_address": { "$ref": "address.json" }
  }
}

1.引用的JSON模式(address.json)

{
  "type": "object",
  "properties": {
    "street_address": { "type": "string" },
    "city":           { "type": "string" },
    "state":          { "type": "string" }
  },
  "required": ["street_address", "city", "state"]
}

这种方法的主要优点是可重用性。

如果我想将这些模式组合成一个**,就会出现问题**。例如,我需要生成一个JSON文件,其中所有支持的字段都有虚拟值。

所以,我想有这样的图式作为结果:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "billing_address": { 
       "street_address": { "type": "string" },
       "city":           { "type": "string" },
       "state":          { "type": "string" }
    }
  }
}

请注意,所有模式都存在于类路径中。
我一直在寻找现有的解决方案,了解如何在Java中实现这一点。不幸的是,大多数库都解决了如何通过POJO生成模式的任务。但在本例中,我需要相反的解决方案

ssgvzors

ssgvzors1#

在两个方向上都有生成器:

  • 从POJO到架构
  • 从架构到POJO

你似乎对这两样都不感兴趣,因为你想要的是:

  • 从模式(部分)到(单个)模式

恐怕你找到现有解决方案的可能性很小。
但你自己应该能做到这一点,特别是如果你能做几个简单的假设:
1.在数据模型中没有名称为$ref的属性。
1.所有的模式部分都在类路径中--为了简单起见:在与执行单独模式部分的合并的Java类相同的包中。
1.主/入口架构的某个被引用的其他架构部分没有对该架构的循环引用。
1.在条目模式的definitions中包含不同的部分是可以接受的。
1.架构部分没有重叠的definitions
该实用程序可能如下所示:

public class SchemaMerger {

    private final ObjectMapper objectMapper = new ObjectMapper();
    private final Map<String, ObjectNode> schemas = new HashMap<>();
    private final List<ObjectNode> definitions = new ArrayList<>();

    public String createConsolidatedSchema(String entrySchemaPath) throws IOException {
        ObjectNode entrySchema = this.getSchemaWithResolvedParts(entrySchemaPath);
        ObjectNode consolidatedSchema = this.objectMapper.createObjectNode().setAll(entrySchema);
        ObjectNode definitionsNode = consolidatedSchema.with("definitions");
        this.definitions.forEach(definitionsNode::setAll);
        for (Map.Entry<String, ObjectNode> schemaPart : this.schemas.entrySet()) {
            // include schema loaded from separate file in definitions
            definitionsNode.set(schemaPart.getKey(), schemaPart.getValue().without("$schema"));
        }
        return consolidatedSchema.toPrettyString();
    }

    private ObjectNode getSchemaWithResolvedParts(String schemaPath) throws IOException {
        ObjectNode entrySchema = (ObjectNode) this.objectMapper.readTree(SchemaMerger.loadResource(schemaPath));
        this.resolveExternalReferences(entrySchema);
        JsonNode definitionsNode = entrySchema.get("definitions");
        if (definitionsNode instanceof ObjectNode) {
            this.definitions.add((ObjectNode) definitionsNode);
            entrySchema.remove("definitions");
        }
        return entrySchema;
    }

    private void resolveExternalReferences(JsonNode schemaPart) throws IOException {
        if (schemaPart instanceof ObjectNode || schemaPart instanceof ArrayNode) {
            // recursively iterate over all nested nodes
            for (JsonNode field : schemaPart) {
                this.resolveExternalReferences(field);
            }
        }
        if (!(schemaPart instanceof ObjectNode)) {
            return;
        }
        JsonNode reference = schemaPart.get("$ref");
        if (reference instanceof TextNode) {
            String referenceValue = reference.textValue();
            if (!referenceValue.startsWith("#")) {
                // convert reference to separate file to entry in definitions
                ((ObjectNode) schemaPart).put("$ref", "#/definitions/" + referenceValue);
                if (!this.schemas.containsKey(referenceValue)) {
                    this.schemas.put(referenceValue, this.getSchemaWithResolvedParts(referenceValue));
                }
            }
        }
    }

    private static String loadResource(String resourcePath) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        try (InputStream inputStream = SchemaMerger.class.getResourceAsStream(resourcePath);
                Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name())) {
            while (scanner.hasNext()) {
                stringBuilder.append(scanner.nextLine()).append('\n');
            }
        }
        return stringBuilder.toString();
    }
}

呼叫new SchemaMerger().createConsolidatedSchema("customer.json")会产生下列结构描述:

{
  "$schema" : "http://json-schema.org/draft-07/schema#",
  "type" : "object",
  "properties" : {
    "billing_address" : {
      "$ref" : "#/definitions/address.json"
    }
  },
  "definitions" : {
    "address.json" : {
      "type" : "object",
      "properties" : {
        "street_address" : {
          "type" : "string"
        },
        "city" : {
          "type" : "string"
        },
        "state" : {
          "type" : "string"
        }
      },
      "required" : [ "street_address", "city", "state" ]
    }
  }
}

这应该为您提供了一个构建所需内容的起点。

cld4siwp

cld4siwp2#

参考:this post。我已经张贴了一个可能的解决方案。
虽然,正如前面提到的,我自己还没有试过。

相关问题