Есть генераторы в обоих направлениях:
- от POJO до схемы
- от схемы до POJO
Вам, похоже, не интересно ни то, ни другое что вы хотите:
- из схемы (части) в (одну) схему
Боюсь, ваши шансы найти существующее решение могут быть небольшими .
Но вы должны быть в состоянии сделать это самостоятельно, особенно если вы можете сделать несколько упрощающих предположений:
- Нигде в вашей модели данных нет свойств с именем
$ref
. - Все части схемы присутствуют в пути к классам - для простоты: в том же пакете, что и класс java, выполняющий объединение отдельной части схемы.
- Существует нет циклической ссылки на вашу основную / входную схему из одной из упомянутых других частей схемы.
- Допустимо включать различные части в
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" ]
}
}
}
Это должно дать вам отправную точку для построения того, что вам нужно.