com.github.fge.avro.translators.RecordTranslator.java Source code

Java tutorial

Introduction

Here is the source code for com.github.fge.avro.translators.RecordTranslator.java

Source

/*
 * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
 *
 * This software is dual-licensed under:
 *
 * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
 *   later version;
 * - the Apache Software License (ASL) version 2.0.
 *
 * The text of both licenses is available under the src/resources/ directory of
 * this project (under the names LGPL-3.0.txt and ASL-2.0.txt respectively).
 *
 * Direct link to the sources:
 *
 * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
 * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
 */

package com.github.fge.avro.translators;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.fge.avro.MutableTree;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jackson.NodeType;
import com.github.fge.jackson.jsonpointer.JsonPointer;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import org.apache.avro.Schema;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;

import java.io.IOException;
import java.util.List;

final class RecordTranslator extends NamedAvroTypeTranslator {
    private static final ObjectMapper OLD_MAPPER = new ObjectMapper();

    private static final AvroTranslator INSTANCE = new RecordTranslator();

    private RecordTranslator() {
        super(Schema.Type.RECORD);
    }

    public static AvroTranslator getInstance() {
        return INSTANCE;
    }

    @Override
    protected void doTranslate(final Schema avroSchema, final MutableTree jsonSchema, final ProcessingReport report)
            throws ProcessingException {
        final List<Schema.Field> fields = avroSchema.getFields();

        if (fields.isEmpty()) {
            final ArrayNode node = FACTORY.arrayNode();
            node.add(FACTORY.objectNode());
            jsonSchema.getCurrentNode().put("enum", node);
            return;
        }

        final JsonPointer pwd = jsonSchema.getPointer();

        if (avroSchema.getDoc() != null)
            jsonSchema.getCurrentNode().put("description", avroSchema.getDoc());

        jsonSchema.setType(NodeType.OBJECT);

        final ArrayNode required = FACTORY.arrayNode();
        jsonSchema.getCurrentNode().put("required", required);

        jsonSchema.getCurrentNode().put("additionalProperties", false);

        final ObjectNode properties = FACTORY.objectNode();
        jsonSchema.getCurrentNode().put("properties", properties);

        String fieldName;
        Schema fieldSchema;
        Schema.Type fieldType;
        AvroTranslator translator;
        JsonPointer ptr;
        ObjectNode propertyNode;
        String s;

        /*
         * FIXME: "default" and readers'/writers' schema? Here, even with a
         * default value, the record field is marked as required.
         */
        for (final Schema.Field field : fields) {
            fieldName = field.name();
            fieldSchema = field.schema();
            fieldType = fieldSchema.getType();
            translator = AvroTranslators.getTranslator(fieldType);
            required.add(fieldName);
            ptr = JsonPointer.of("properties", fieldName);
            propertyNode = FACTORY.objectNode();
            properties.put(fieldName, propertyNode);
            injectDefault(propertyNode, field);
            jsonSchema.setPointer(pwd.append(ptr));
            translator.translate(fieldSchema, jsonSchema, report);
            jsonSchema.setPointer(pwd);
        }
    }

    private static void injectDefault(final ObjectNode propertyNode, final Schema.Field field) {
        final JsonNode value = field.defaultValue();
        if (value == null)
            return;

        /*
         * Write the value to a string using a 1.8 writer, and read it from that
         * string using a 2.1 reader... Did you say "hack"?
         */
        try {
            final String s = OLD_MAPPER.writeValueAsString(value);
            propertyNode.put("default", JsonLoader.fromString(s));
        } catch (IOException ignored) {
            // cannot happen
        }
    }
}