All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.zuunr.jsonschema.JsonSchemaConverter Maven / Gradle / Ivy

/*
 * Copyright 2020 Zuunr AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.zuunr.jsonschema;

import com.zuunr.forms.Form;
import com.zuunr.forms.FormField;
import com.zuunr.forms.FormFields;
import com.zuunr.forms.ValueFormat;
import com.zuunr.forms.formfield.Type;
import com.zuunr.json.JsonArray;
import com.zuunr.json.JsonObject;
import com.zuunr.json.JsonObjectBuilder;
import com.zuunr.json.JsonValue;

import java.util.Iterator;

/**
 * @author Niklas Eldberger
 */
public class JsonSchemaConverter {

    private static final JsonValue JSON_SCHEMA_TYPE_NULL = JsonValue.of("null");
    private static final JsonObject JSON_SCHEMA_VALIDATION_KEY_MAPPINGS = JsonObject.EMPTY.builder()
            .put("maximum", "max")
            .put("exclusiveMaximum", "exclusiveMaximum")
            .put("maxItems", "maxsize")
            .put("maxLength", "maxlength")
            .put("minimum", "min")
            .put("exclusiveMinimum", "exclusiveMinimum")
            .put("minItems", "minsize")
            .put("minLength", "minlength")
            .put("nullable", "nullable")
            .put("pattern", "pattern")
            .put("description", "desc")
            .put("enum", "enum")
            .put("const", "const")
            .put("format", "typeFormat")
            .put("default", "default")
            .build();

    private static final JsonObject JSON_SCHEMA_SUBSCHEMA_KEYWORDS_SUPPORT = init(
            JsonObject.EMPTY.builder()
                    .put("allOf", false)
                    .put("anyOf", false)
                    .put("oneOf", false)
                    .put("not", false)
                    .put("if", false)
                    .put("then", false)
                    .put("else", false)
                    .put("dependentSchemas", false)
                    .put("items", true)
                    .put("additionalItems", true)
                    .put("unevaluatedItems", false)
                    .put("contains", false)
                    .put("properties", true)
                    .put("patternProperties", false)
                    .put("additionalProperties", true)
                    .put("propertyNames", false)
                    .put("type", true)
                    .put("required", true)
                    .build(),
            JSON_SCHEMA_VALIDATION_KEY_MAPPINGS);

    public static JsonObject init(JsonObject subschemaKeywords, JsonObject keyMappings) {
        JsonObjectBuilder builder = subschemaKeywords.builder();
        for (String key : keyMappings.keys().asList(String.class)) {
            builder.put(key, true);
        }
        return builder.build();
    }

    public ValueFormat translate(JsonValue jsonSchema) {
        if (jsonSchema.is(Boolean.class)) {
            translate(jsonSchema.getValue(Boolean.class));
        } else if (jsonSchema.is(JsonObject.class)) {
            return translate(jsonSchema.getValue(JsonObject.class));
        }
        throw new ConversionException("Only Boolean and JsonObject is supported right now");
    }

    public ValueFormat translate(boolean jsonSchema) {
        if (jsonSchema) {
            return ValueFormat.ANY_VALUE_FORMAT;
        } else {
            return ValueFormat.IMPOSSIBLE_VALUE_FORMAT;
        }
    }

    public ValueFormat translate(JsonObject jsonSchema) {
        return translateToValueFormat(jsonSchema).build().as(ValueFormat.class);
    }

    private JsonObjectBuilder translateToValueFormat(JsonObject jsonSchema) {
        JsonObjectBuilder formFieldOrValueFormat = JsonObject.EMPTY.builder();
        return translateToValueFormat(jsonSchema, formFieldOrValueFormat);
    }

    private JsonObjectBuilder translateToValueFormat(JsonObject jsonSchema, JsonObjectBuilder formFieldOrValueFormat) {

        JsonObject unsupported = validateSchemaSupport(jsonSchema);
        if (!unsupported.isEmpty()) {
            throw new ConversionException("JSON Schema contains unsupported properties: " + unsupported);
        }

        translateSimpleValidationKeywords(jsonSchema, formFieldOrValueFormat);

        Type type = translateType(jsonSchema, formFieldOrValueFormat);
        formFieldOrValueFormat.put("type", type);

        if (type.isString()) {
            // Mapping is one-to-one, no need for mapping enum to pattern :)

        } else if (type.isObject()) {
            translateObject(jsonSchema, formFieldOrValueFormat);
        } else if (type.isArrayOrSet()) {
            translateArray(jsonSchema, formFieldOrValueFormat);
        }
        return formFieldOrValueFormat;
    }

    private JsonObject validateSchemaSupport(JsonObject jsonSchema) {

        JsonObjectBuilder unsupported = JsonObject.EMPTY.builder();

        Iterator values = jsonSchema.values().iterator();
        for (String key : jsonSchema.keys().asList(String.class)) {

            JsonValue value = values.next();
            if (!JSON_SCHEMA_SUBSCHEMA_KEYWORDS_SUPPORT.get(key, JsonValue.FALSE).getValue(Boolean.class).booleanValue()) {
                unsupported.put(key, value);
            }
        }
        return unsupported.build();
    }

    private Type translateType(JsonObject jsonSchemaProperty, JsonObjectBuilder jsonObjectBuilder) {

        JsonArray jsonSchemaTypes;
        Type formatValueType = null;

        JsonValue jsonSchemaTypeJsonValue = jsonSchemaProperty.get("type", JsonArray.EMPTY);

        if (jsonSchemaTypeJsonValue.isString()) {
            jsonSchemaTypes = JsonArray.of(jsonSchemaTypeJsonValue);
        } else {
            jsonSchemaTypes = jsonSchemaTypeJsonValue.getValue(JsonArray.class);
        }

        for (JsonValue typeJsonValue : jsonSchemaTypes) {
            String type = typeJsonValue.getString();
            if (JSON_SCHEMA_TYPE_NULL.equals(typeJsonValue)) {
                jsonObjectBuilder.put("nullable", true);
            } else if (formatValueType == null) {
                formatValueType = translateNonNullType(type);
            } else {
                // Forms do not support multi-types
                throw new ConversionException("Multi-types " + jsonSchemaTypes + "cannot be converted.");
            }
        }
        return formatValueType == null ? Type.UNDEFINED : formatValueType;
    }

    private Type translateNonNullType(String type) {
        if (Type.STRING.toString().equals(type)) {
            return Type.STRING;
        } else if (Type.OBJECT.toString().equals(type)) {
            return Type.OBJECT;
        } else if (Type.ARRAY.toString().equals(type)) {
            return Type.ARRAY;
        } else if (Type.INTEGER.toString().equals(type)) {
            return Type.INTEGER;
        } else if (Type.BOOLEAN.toString().equals(type)) {
            return Type.BOOLEAN;
        } else {
            throw new ConversionException("Not supported type '" + type + "'");
        }

    }

    public void translateObject(JsonObject objectJsonSchema, JsonObjectBuilder formFieldOrValueFormat) {

        FormFields.FormFieldsBuilder formFieldsBuilder = FormFields.EMPTY.builder();

        JsonObject objectProperties = requiredAsObject(objectJsonSchema.get("required", JsonArray.EMPTY).getValue(JsonArray.class)).putAll(objectJsonSchema.get("properties", JsonObject.EMPTY).getValue(JsonObject.class));

        JsonArray keys = objectProperties.keys();
        Iterator values = objectProperties.values().iterator();

        for (JsonValue keyJsonValue : keys) {
            String key = keyJsonValue.getString();
            JsonObject value = values.next().getValue(JsonObject.class);
            formFieldsBuilder.add(
                    translateToValueFormat(value)
                            .put("name", key)
                            .put("required", isRequired(objectJsonSchema, key))
                            .build()
                            .as(FormField.class));
        }
        Form.FormBuilder formBuilder = Form.EMPTY.builder().value(formFieldsBuilder);

        boolean exclusive;
        JsonValue additionalProperties = objectJsonSchema.get("additionalProperties", JsonValue.FALSE);
        if (additionalProperties.is(Boolean.class)) {
            exclusive = !additionalProperties.getValue(Boolean.class);
        } else {
            exclusive = additionalProperties.equals(JsonObject.EMPTY.put("not", JsonObject.EMPTY).jsonValue());
        }
        formBuilder.exclusive(exclusive);
        formFieldOrValueFormat.put("form", formBuilder.build());
    }

    private JsonObject requiredAsObject(JsonArray jsonSchemaRequired) {
        JsonObjectBuilder builder = JsonObject.EMPTY.builder();
        for (String requiredProperty : jsonSchemaRequired.asList(String.class)) {
            builder.put(requiredProperty, JsonObject.EMPTY);
        }
        return builder.build();
    }

    private boolean isRequired(JsonObject objectJsonSchema, String property) {
        return objectJsonSchema.get("required", JsonArray.EMPTY).getValue(JsonArray.class).contains(property);
    }

    public void translateArray(JsonObject arrayJsonSchema, JsonObjectBuilder formFieldOrValueFormat) {

        JsonObject items = arrayJsonSchema.get("items", JsonObject.EMPTY).getValue(JsonObject.class);
        ValueFormat valueFormat = translateToValueFormat(items).build().as(ValueFormat.class);
        formFieldOrValueFormat.put("element", valueFormat);
    }

    private void translateSimpleValidationKeywords(JsonObject jsonSchema, JsonObjectBuilder formFieldOrValueFormat) {

        JsonArray keys = jsonSchema.keys();
        Iterator values = jsonSchema.values().iterator();

        for (String jsonSchemaKey : keys.asList(String.class)) {
            JsonValue asFormKey = JSON_SCHEMA_VALIDATION_KEY_MAPPINGS.get(jsonSchemaKey);
            JsonValue value = values.next();
            if (asFormKey != null) {
                formFieldOrValueFormat.put(asFormKey.getString(), value);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy