com.nextdoor.bender.deserializer.json.GenericJsonDeserializer.java Source code

Java tutorial

Introduction

Here is the source code for com.nextdoor.bender.deserializer.json.GenericJsonDeserializer.java

Source

/*
 * 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.
 *
 * Copyright 2017 Nextdoor.com, Inc
 *
 */

package com.nextdoor.bender.deserializer.json;

import java.util.List;

import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import com.nextdoor.bender.deserializer.DeserializationException;
import com.nextdoor.bender.deserializer.DeserializedEvent;
import com.nextdoor.bender.deserializer.Deserializer;
import com.nextdoor.bender.deserializer.json.GenericJsonDeserializerConfig.FieldConfig;

/**
 * Converts a JSON string into a JsonElement object.
 */
public class GenericJsonDeserializer extends Deserializer {
    protected JsonParser parser;
    private final List<FieldConfig> nestedFieldConfigs;
    private String rootNodeOverridePath;

    public GenericJsonDeserializer(List<FieldConfig> nestedFieldConfigs) {
        this(nestedFieldConfigs, null);
    }

    public GenericJsonDeserializer(List<FieldConfig> nestedFieldConfigs, String rootNodeOverridePath) {
        this.nestedFieldConfigs = nestedFieldConfigs;
        this.rootNodeOverridePath = rootNodeOverridePath;
    }

    @Override
    public DeserializedEvent deserialize(String raw) {
        GenericJsonEvent devent = new GenericJsonEvent(null);

        JsonElement elm;
        try {
            elm = parser.parse(raw);
        } catch (JsonSyntaxException e) {
            throw new DeserializationException(e);
        }

        if (!elm.isJsonObject()) {
            throw new DeserializationException("event is not a json object");
        }

        JsonObject obj = elm.getAsJsonObject();

        /*
         * Convert fields which are nested json strings into json objects
         */
        for (FieldConfig fconfig : this.nestedFieldConfigs) {
            if (obj.has(fconfig.getField())) {
                JsonElement msg = obj.get(fconfig.getField());

                /*
                 * Find the JSON in the string and replace the field
                 */
                NestedData data = deserialize(msg);

                obj.remove(fconfig.getField());
                obj.add(fconfig.getField(), data.nested);

                /*
                 * If the string contained data before the JSON store it in a new field
                 */
                if (fconfig.getPrefixField() != null && data.prefix != null) {
                    obj.add(fconfig.getPrefixField(), data.prefix);
                }
            }
        }

        if (rootNodeOverridePath != null) {
            Object o = JsonPathProvider.read(obj, rootNodeOverridePath);
            if (obj == null || o instanceof JsonNull) {
                throw new DeserializationException(rootNodeOverridePath + " path not found in object");
            }
            obj = (JsonObject) o;
        }

        devent.setPayload(obj);

        return devent;
    }

    /**
     * Initializes the Gson JsonParser.
     */
    @Override
    public void init() {
        this.parser = new JsonParser();
    }

    /**
     * Stores the parsed string as a JSON object as well any prefix that may have proceeded the JSON
     * in the string.
     */
    protected static class NestedData {
        public JsonElement nested;
        public JsonPrimitive prefix;

        public NestedData(JsonElement nested) {
            this.nested = nested;
        }

        public NestedData(JsonElement nested, String prefix) {
            this.nested = nested;
            this.prefix = new JsonPrimitive(prefix);
        }
    }

    private NestedData deserialize(JsonElement json) throws JsonParseException {
        final String messageStr = json.getAsString();
        /*
         * Find what might be JSON
         */
        int braceIndex = messageStr.indexOf('{');

        if (braceIndex == -1) {
            return new NestedData(json);
        }

        String maybeNestedJson = messageStr.substring(braceIndex);

        /*
         * Attempt to deserialize what we think might be JSON
         */
        JsonElement nestedJson;
        try {
            nestedJson = parser.parse(maybeNestedJson);
        } catch (Exception e) {
            return new NestedData(json);
        }

        /*
         * Only JSON objects are supported for MESSAGE payloads. No primitives or arrays.
         */
        if (!nestedJson.isJsonObject()) {
            return new NestedData(json);
        } else {
            return new NestedData(nestedJson.getAsJsonObject(), messageStr.substring(0, braceIndex));
        }
    }
}