Java: десериализация составной JSON схемы с $ ref для одной схемы - PullRequest
2 голосов
/ 23 апреля 2020

Согласно Структурирование сложной схемы , возможно иметь следующее соотношение:

  1. База JSON Схема (заказчик. json)
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "billing_address": { "$ref": "address.json" }
  }
}

Ссылка JSON Схема (адрес. 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. Но в этом случае мне нужен обратный

1 Ответ

2 голосов
/ 23 апреля 2020

Есть генераторы в обоих направлениях:

  • от POJO до схемы
  • от схемы до POJO

Вам, похоже, не интересно ни то, ни другое что вы хотите:

  • из схемы (части) в (одну) схему

Боюсь, ваши шансы найти существующее решение могут быть небольшими .
Но вы должны быть в состоянии сделать это самостоятельно, особенно если вы можете сделать несколько упрощающих предположений:

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

Это должно дать вам отправную точку для построения того, что вам нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...