com.balajeetm.mystique.util.gson.lever.JsonLever.java Source code

Java tutorial

Introduction

Here is the source code for com.balajeetm.mystique.util.gson.lever.JsonLever.java

Source

/*
 * Copyright (c) Balajee TM 2017.
 * All rights reserved.
 * License -  @see <a href="http://www.apache.org/licenses/LICENSE-2.0"></a>
 */

/*
 * Created on 15 Oct, 2017 by balajeemohan
 * http://www.balajeetm.com
 */
package com.balajeetm.mystique.util.gson.lever;

import java.text.DateFormat;
import java.util.Iterator;
import java.util.Map.Entry;

import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import com.balajeetm.mystique.util.gson.GsonFactory;
import com.balajeetm.mystique.util.gson.convertor.GsonConvertor;
import com.balajeetm.mystique.util.json.error.ConvertorException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/** The Constant log. */

/** The Constant log. */
@Slf4j
public class JsonLever {

    /**
     * Gets the json parser.
     *
     * @return the json parser
     */

    /**
     * Gets the json parser.
     *
     * @return the json parser
     */
    @Getter
    private JsonParser jsonParser;

    /**
     * Gets the gson.
     *
     * @return the gson
     */

    /**
     * Gets the gson.
     *
     * @return the gson
     */
    @Getter
    private Gson gson;

    /**
     * Gets the single instance of JsonLever.
     *
     * @return single instance of JsonLever
     */
    public static JsonLever getInstance() {
        return Creator.INSTANCE;
    }

    // Efficient Thread safe Lazy Initialization
    // works only if the singleton constructor is non parameterized
    /** The Class Creator. */
    private static class Creator {

        /** The instance. */
        private static JsonLever INSTANCE = new JsonLever();
    }

    /** Instantiates a new json lever. */
    protected JsonLever() {
        jsonParser = GsonFactory.getInstance().getJsonParser();
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setDateFormat(DateFormat.LONG, DateFormat.LONG);
        gson = gsonBuilder.create();
    }

    /**
     * Gets the json element in the specified jpath.
     *
     * @param source the json source
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the json element at the specified path
     */
    public JsonElement get(JsonElement source, String jpath) {
        if (StringUtils.equals(".", jpath)) {
            return source;
        }
        return get(source, getJpath(jpath));
    }

    /**
     * Gets the json element in the specified jpath.
     *
     * @param source the source, can be a POJO, json string, or a JsonElement
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the json element at the specified path
     */
    public JsonElement get(Object source, String jpath) {
        return get(jsonify(source), jpath);
    }

    /**
     * Gets the json element in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1]) is '2'. Array indexes need to be specified as numerals.
     *     Strings are always presumed to be field names.
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonElement get(JsonElement source, JsonArray jpath) {
        JsonElement result = JsonNull.INSTANCE;
        if (isNotNull(jpath)) {
            result = source;
            for (JsonElement path : jpath) {
                try {
                    if (isNumber(path)) {
                        result = result.getAsJsonArray().get(path.getAsInt());
                    } else {
                        result = result.getAsJsonObject().get(path.getAsString());
                    }
                } catch (Exception e) {
                    return JsonNull.INSTANCE;
                }
            }
        }

        return result;
    }

    /**
     * Gets the json element in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1]) is '2'. Array indexes need to be specified as numerals.
     *     Strings are always presumed to be field names.
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonElement get(Object source, JsonArray jpath) {
        return get(jsonify(source), jpath);
    }

    /**
     * Gets the json element in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonElement get(JsonElement source, Object... jpath) {
        return get(source, newJsonArray(jpath));
    }

    /**
     * Gets the json element in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonElement get(Object source, Object... jpath) {
        return get(jsonify(source), newJsonArray(jpath));
    }

    /**
     * Gets the json element in the specified jpath or returns a default value.
     *
     * @param source the source
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @param defaultValue the default value
     * @return the json element returns defaultValue for any invalid path or if the item at specified
     *     location is Null return the source if the input jpath is '.'
     */
    public JsonElement get(JsonElement source, String jpath, JsonElement defaultValue) {
        JsonElement result = get(source, jpath);
        return isNull(result) ? defaultValue : result;
    }

    /**
     * Gets the json element in the specified jpath oe returns a default value.
     *
     * @param source the source, can be a POJO, json string, or a JsonElement
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @param defaultValue the default value
     * @return the json element returns defaultValue for any invalid path or if the item at specified
     *     location is Null return the source if the input jpath is '.'
     */
    public JsonElement get(Object source, String jpath, JsonElement defaultValue) {
        return get(jsonify(source), jpath, defaultValue);
    }

    /**
     * Gets the.
     *
     * <p>Gets the json element in the specified jpath or returns a default value
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1]) is '2'. Array indexes need to be specified as numerals.
     *     Strings are always presumed to be field names.
     * @param defaultValue the default value
     * @return the json element returns defaultValue for any invalid path or if the item at specified
     *     location is Null return the source if the input jpath is '.'
     */
    public JsonElement get(JsonElement source, JsonArray jpath, JsonElement defaultValue) {
        JsonElement result = get(source, jpath);
        return isNull(result) ? defaultValue : result;
    }

    /**
     * Gets the.
     *
     * <p>Gets the json element in the specified jpath or returns a default value
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1]) is '2'. Array indexes need to be specified as numerals.
     *     Strings are always presumed to be field names.
     * @param defaultValue the default value
     * @return the json element returns defaultValue for any invalid path or if the item at specified
     *     location is Null return the source if the input jpath is '.'
     */
    public JsonElement get(Object source, JsonArray jpath, JsonElement defaultValue) {
        return get(jsonify(source), jpath, defaultValue);
    }

    /**
     * Sets the json element at the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a.b.c.1", 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Strings that can be
     *     casted to numerals are presumed to be array indexes
     * @param value the value
     * @return the json element
     */
    public JsonElement set(JsonElement source, String jpath, JsonElement value) {
        return set(source, getJpath(jpath), value);
    }

    /**
     * Sets the string at the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a.b.c.1", "5"]) is {'a': {'b': {'c': [1, "5", 3, 4]}}}. Strings that can be
     *     casted to numerals are presumed to be array indexes
     * @param value the value
     * @return the json element
     */
    public JsonElement set(JsonElement source, String jpath, String value) {
        return set(source, jpath, new JsonPrimitive(value));
    }

    /**
     * Sets the string at the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a.b.c.1", true]) is {'a': {'b': {'c': [1, true, 3, 4]}}}. Strings that can be
     *     casted to numerals are presumed to be array indexes
     * @param value the value
     * @return the json element
     */
    public JsonElement set(JsonElement source, String jpath, Boolean value) {
        return set(source, jpath, new JsonPrimitive(value));
    }

    /**
     * Sets the string at the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a.b.c.1", 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Strings that can be
     *     casted to numerals are presumed to be array indexes
     * @param value the value
     * @return the json element
     */
    public JsonElement set(JsonElement source, String jpath, Number value) {
        return set(source, jpath, new JsonPrimitive(value));
    }

    /**
     * Sets the json element at the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @param value the value
     * @return the json element
     */
    public JsonElement set(JsonElement source, JsonArray jpath, JsonElement value) {
        JsonElement result = JsonNull.INSTANCE;
        if (isNotNull(jpath)) {
            result = source;
            JsonElement field = result;
            if (jpath.size() > 0) {
                JsonElement previousPath = null;
                JsonElement currentPath = null;
                Iterator<JsonElement> iterator = jpath.iterator();
                if (iterator.hasNext()) {
                    previousPath = iterator.next();
                }

                while (iterator.hasNext()) {
                    currentPath = iterator.next();
                    // get the field
                    field = getRepleteField(field, previousPath, currentPath);
                    result = updateResult(result, field);
                    field = isNumber(previousPath) ? field.getAsJsonArray().get(previousPath.getAsInt())
                            : field.getAsJsonObject().get(previousPath.getAsString());
                    previousPath = currentPath;
                }

                field = setField(field, previousPath, value);
            }
        }
        return result;
    }

    /**
     * Sets the string at the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a.b.c.1", "5"]) is {'a': {'b': {'c': [1, "5", 3, 4]}}}. Strings that can be
     *     casted to numerals are presumed to be array indexes
     * @param value the value
     * @return the json element
     */
    public JsonElement set(JsonElement source, JsonArray jpath, String value) {
        return set(source, jpath, new JsonPrimitive(value));
    }

    /**
     * Sets the string at the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a.b.c.1", true]) is {'a': {'b': {'c': [1, true, 3, 4]}}}. Strings that can be
     *     casted to numerals are presumed to be array indexes
     * @param value the value
     * @return the json element
     */
    public JsonElement set(JsonElement source, JsonArray jpath, Boolean value) {
        return set(source, jpath, new JsonPrimitive(value));
    }

    /**
     * Sets the string at the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a.b.c.1", 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Strings that can be
     *     casted to numerals are presumed to be array indexes
     * @param value the value
     * @return the json element
     */
    public JsonElement set(JsonElement source, JsonArray jpath, Number value) {
        return set(source, jpath, new JsonPrimitive(value));
    }

    /**
     * Returns the JsonObject identified by the fully qualified json path as a json object if
     * possible. Else returns null
     *
     * @param source the json source element
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public JsonObject getJsonObject(JsonElement source, String jpath) {
        return asJsonObject(get(source, jpath));
    }

    /**
     * Returns the JsonObject identified by the fully qualified json path as a json object if
     * possible. Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public JsonObject getJsonObject(Object source, String jpath) {
        return asJsonObject(get(source, jpath));
    }

    /**
     * Returns the JsonObject identified by the fully qualified json path as a json object if
     * possible. Else returns null
     *
     * @param source the json source element
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public JsonObject getJsonObject(JsonElement source, JsonArray jpath) {
        return asJsonObject(get(source, jpath));
    }

    /**
     * Returns the JsonObject identified by the fully qualified json path as a json object if
     * possible. Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public JsonObject getJsonObject(Object source, JsonArray jpath) {
        return asJsonObject(get(source, jpath));
    }

    /**
     * Gets the json object in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonObject getJsonObject(JsonElement source, Object... jpath) {
        return asJsonObject(get(source, newJsonArray(jpath)));
    }

    /**
     * Gets the json object in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonObject getJsonObject(Object source, Object... jpath) {
        return asJsonObject(get(jsonify(source), newJsonArray(jpath)));
    }

    /**
     * Returns the JsonArray identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public JsonArray getJsonArray(JsonElement source, String jpath) {
        return asJsonArray(get(source, jpath));
    }

    /**
     * Returns the JsonArray identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public JsonArray getJsonArray(Object source, String jpath) {
        return asJsonArray(get(source, jpath));
    }

    /**
     * Returns the JsonArray identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public JsonArray getJsonArray(JsonElement source, JsonArray jpath) {
        return asJsonArray(get(source, jpath));
    }

    /**
     * Returns the JsonArray identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public JsonArray getJsonArray(Object source, JsonArray jpath) {
        return asJsonArray(get(source, jpath));
    }

    /**
     * Gets the JsonArray in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonArray getJsonArray(JsonElement source, Object... jpath) {
        return asJsonArray(get(source, newJsonArray(jpath)));
    }

    /**
     * Gets the JsonArray in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonArray getJsonArray(Object source, Object... jpath) {
        return asJsonArray(get(jsonify(source), newJsonArray(jpath)));
    }

    /**
     * Returns the String identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public String getString(JsonElement source, String jpath) {
        return asString(get(source, jpath));
    }

    /**
     * Returns the String identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public String getString(Object source, String jpath) {
        return asString(get(source, jpath));
    }

    /**
     * Returns the String identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public String getString(JsonElement source, JsonArray jpath) {
        return asString(get(source, jpath));
    }

    /**
     * Returns the String identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public String getString(Object source, JsonArray jpath) {
        return asString(get(source, jpath));
    }

    /**
     * Gets the String in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public String getString(JsonElement source, Object... jpath) {
        return asString(get(source, newJsonArray(jpath)));
    }

    /**
     * Gets the String in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public String getString(Object source, Object... jpath) {
        return asString(get(jsonify(source), newJsonArray(jpath)));
    }

    /**
     * Returns the Long identified by the fully qualified json path as a json object if possible. Else
     * returns null
     *
     * @param source the json source element
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public Long getLong(JsonElement source, String jpath) {
        return asLong(get(source, jpath));
    }

    /**
     * Returns the Long identified by the fully qualified json path as a json object if possible. Else
     * returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public Long getLong(Object source, String jpath) {
        return asLong(get(source, jpath));
    }

    /**
     * Returns the Long identified by the fully qualified json path as a json object if possible. Else
     * returns null
     *
     * @param source the json source element
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public Long getLong(JsonElement source, JsonArray jpath) {
        return asLong(get(source, jpath));
    }

    /**
     * Returns the Long identified by the fully qualified json path as a json object if possible. Else
     * returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public Long getLong(Object source, JsonArray jpath) {
        return asLong(get(source, jpath));
    }

    /**
     * Gets the Long in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public Long getLong(JsonElement source, Object... jpath) {
        return asLong(get(source, newJsonArray(jpath)));
    }

    /**
     * Gets the Long in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public Long getLong(Object source, Object... jpath) {
        return asLong(get(jsonify(source), newJsonArray(jpath)));
    }

    /**
     * Returns the Boolean identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public Boolean getBoolean(JsonElement source, String jpath) {
        return asBoolean(get(source, jpath));
    }

    /**
     * Returns the Boolean identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public Boolean getBoolean(Object source, String jpath) {
        return asBoolean(get(source, jpath));
    }

    /**
     * Returns the Boolean identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public Boolean getBoolean(JsonElement source, JsonArray jpath) {
        return asBoolean(get(source, jpath));
    }

    /**
     * Returns the Boolean identified by the fully qualified json path as a json object if possible.
     * Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public Boolean getBoolean(Object source, JsonArray jpath) {
        return asBoolean(get(source, jpath));
    }

    /**
     * Gets the Boolean in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public Boolean getBoolean(JsonElement source, Object... jpath) {
        return asBoolean(get(source, newJsonArray(jpath)));
    }

    /**
     * Gets the Boolean in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public Boolean getBoolean(Object source, Object... jpath) {
        return asBoolean(get(jsonify(source), newJsonArray(jpath)));
    }

    /**
     * Returns the JsonPrimitive identified by the fully qualified json path as a json object if
     * possible. Else returns null
     *
     * @param source the json source element
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public JsonPrimitive getJsonPrimitive(JsonElement source, String jpath) {
        return asJsonPrimitive(get(source, jpath));
    }

    /**
     * Returns the JsonPrimitive identified by the fully qualified json path as a json object if
     * possible. Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath '.' separated string defining the fully qualified json path to the field required.
     *     eg get({'a': {'b': {'c': [1, 2, 3, 4]}}}, 'a.b.c.1') is '2'
     * @return the field of interest as json object
     */
    public JsonPrimitive getJsonPrimitive(Object source, String jpath) {
        return asJsonPrimitive(get(source, jpath));
    }

    /**
     * Returns the JsonPrimitive identified by the fully qualified json path as a json object if
     * possible. Else returns null
     *
     * @param source the json source element
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public JsonPrimitive getJsonPrimitive(JsonElement source, JsonArray jpath) {
        return asJsonPrimitive(get(source, jpath));
    }

    /**
     * Returns the JsonPrimitive identified by the fully qualified json path as a json object if
     * possible. Else returns null
     *
     * @param source the json source element. Can be any Java POJO, that can be jsonified, or a String
     *     representing a valid json
     * @param jpath the fully qualified json path to the field required. eg set({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, ["a", "b" "c", 1, 5]) is {'a': {'b': {'c': [1, 5, 3, 4]}}}. Array indexes need
     *     to be specified as numerals. Strings are always presumed to be field names.
     * @return the field of interest as json object
     */
    public JsonPrimitive getJsonPrimitive(Object source, JsonArray jpath) {
        return asJsonPrimitive(get(source, jpath));
    }

    /**
     * Gets the JsonPrimitive in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonPrimitive getJsonPrimitive(JsonElement source, Object... jpath) {
        return asJsonPrimitive(get(source, newJsonArray(jpath)));
    }

    /**
     * Gets the JsonPrimitive in the specified jpath.
     *
     * @param source the source
     * @param jpath the fully qualified json path to the field required. eg get({'a': {'b': {'c': [1,
     *     2, 3, 4]}}}, "a", "b" "c", 1]) is '2'. Numerals are presumed to be array indexes
     * @return the json element returns 'Null' for any invalid path return the source if the input
     *     jpath is '.'
     */
    public JsonPrimitive getJsonPrimitive(Object source, Object... jpath) {
        return asJsonPrimitive(get(jsonify(source), newJsonArray(jpath)));
    }

    /**
     * Checks if is null.
     *
     * @param source the source
     * @return the boolean
     */
    public Boolean isNull(JsonElement source) {
        return null == source || source.isJsonNull();
    }

    /**
     * Checks if is not null.
     *
     * @param source the source
     * @return the boolean
     */
    public Boolean isNotNull(JsonElement source) {
        return !isNull(source);
    }

    /**
     * Checks if is json array.
     *
     * @param source the source
     * @return the boolean
     */
    public Boolean isArray(JsonElement source) {
        return isNotNull(source) && source.isJsonArray();
    }

    /**
     * Checks if is json object.
     *
     * @param source the source
     * @return the boolean
     */
    public Boolean isObject(JsonElement source) {
        return isNotNull(source) && source.isJsonObject();
    }

    /**
     * Checks if is json primitive.
     *
     * @param source the source
     * @return the boolean
     */
    public Boolean isPrimitive(JsonElement source) {
        return isNotNull(source) && source.isJsonPrimitive();
    }

    /**
     * Checks if is number.
     *
     * @param source the source
     * @return the boolean
     */
    public Boolean isNumber(JsonElement source) {
        return isNotNull(source) && source.isJsonPrimitive() && source.getAsJsonPrimitive().isNumber();
    }

    /**
     * Checks if is string.
     *
     * @param source the source
     * @return the boolean
     */
    public Boolean isString(JsonElement source) {
        return isNotNull(source) && source.isJsonPrimitive() && source.getAsJsonPrimitive().isString();
    }

    /**
     * Checks if is boolean.
     *
     * @param source the source
     * @return the boolean
     */
    public Boolean isBoolean(JsonElement source) {
        return isNotNull(source) && source.isJsonPrimitive() && source.getAsJsonPrimitive().isBoolean();
    }

    /**
     * Returns the source json as a json primitive if possible. Else returns null
     *
     * @param source the source json element
     * @return the source json as a json primitive
     */
    public JsonPrimitive asJsonPrimitive(JsonElement source) {
        return asJsonPrimitive(source, null);
    }

    /**
     * Returns the source json as a json primitive if possible. Else returns the default value
     *
     * @param source the source
     * @param defaultValue the default value
     * @return the json primitive
     */
    public JsonPrimitive asJsonPrimitive(JsonElement source, JsonPrimitive defaultValue) {
        return isPrimitive(source) ? source.getAsJsonPrimitive() : defaultValue;
    }

    /**
     * Returns the source json as a json object if possible. Else returns null
     *
     * @param element the element
     * @return the source json as a json object
     */
    public JsonObject asJsonObject(JsonElement element) {
        return asJsonObject(element, (JsonObject) null);
    }

    /**
     * Returns the source json as a json object if possible. Else returns the default json
     *
     * @param source the source json element
     * @param defaultJson the default json
     * @return the source json as a json object
     */
    public JsonObject asJsonObject(JsonElement source, JsonObject defaultJson) {
        return isObject(source) ? source.getAsJsonObject() : defaultJson;
    }

    /**
     * Returns the source json as a json array if possible. Else returns null
     *
     * @param source the source json element
     * @return the source json as a json array
     */
    public JsonArray asJsonArray(JsonElement source) {
        return asJsonArray(source, (JsonArray) null);
    }

    /**
     * Returns the source json as a json array if possible. Else returns the default json
     *
     * @param source the source json element
     * @param defaultArray the default array
     * @return the source json as a json array
     */
    public JsonArray asJsonArray(JsonElement source, JsonArray defaultArray) {
        return isArray(source) ? source.getAsJsonArray() : defaultArray;
    }

    /**
     * Returns the source json as a json element if not null. Else returns the default json
     *
     * @param source the source json element
     * @param defaultJson the default json
     * @return the source json as a non null json element
     */
    public JsonElement asJsonElement(JsonElement source, JsonElement defaultJson) {
        return isNotNull(source) ? source : defaultJson;
    }

    /**
     * Returns the source json as a string if possible. Else returns null
     *
     * @param source the source json element
     * @return the source json as a string
     */
    public String asString(JsonElement source) {
        return asString(source, (String) null);
    }

    /**
     * Returns the source json as a string if possible. Else returns the default string
     *
     * @param source the source json element
     * @param defaultStr the default string
     * @return the source json as a string
     */
    public String asString(JsonElement source, String defaultStr) {
        return isString(source) ? source.getAsString() : defaultStr;
    }

    /**
     * Returns the source json as a boolean if possible. Else returns null
     *
     * @param source the source json element
     * @return the source json as a boolean
     */
    public Boolean asBoolean(JsonElement source) {
        return asBoolean(source, (Boolean) null);
    }

    /**
     * Returns the source json as a boolean if possible. Else returns the default boolean
     *
     * @param source the source json element
     * @param defaultBool the default boolean
     * @return the source json as a boolean
     */
    public Boolean asBoolean(JsonElement source, Boolean defaultBool) {
        return isBoolean(source) ? (Boolean) source.getAsBoolean() : defaultBool;
    }

    /**
     * Returns the source json as a long if possible. Else returns null
     *
     * @param source the source json element
     * @return the source json as a long
     */
    public Long asLong(JsonElement source) {
        return asLong(source, (Long) null);
    }

    /**
     * Returns the source json as a long if possible. Else returns the default long
     *
     * @param source the source json element
     * @param defaultLong the default long
     * @return the source json as a long
     */
    public Long asLong(JsonElement source, Long defaultLong) {
        return isNumber(source) ? (Long) source.getAsLong() : defaultLong;
    }

    /**
     * Returns the source json as an integer if possible. Else returns null
     *
     * @param source the source json element
     * @return the source json as an integer
     */
    public Integer asInt(JsonElement source) {
        return asInt(source, (Integer) null);
    }

    /**
     * Returns the source json as an Integer if possible. Else returns the default integer
     *
     * @param source the source json element
     * @param defaultInt the default integer
     * @return the source json as an integer
     */
    public Integer asInt(JsonElement source, Integer defaultInt) {
        return isNumber(source) ? (Integer) source.getAsInt() : defaultInt;
    }

    /**
     * Gets the first json element, if any, in the list. Else returns null
     *
     * @param elements the json elements
     * @return the first json element in the list
     */
    public JsonElement getFirst(Iterable<JsonElement> elements) {
        JsonElement first = null;
        if (!IterableUtils.isEmpty(elements)) {
            first = IterableUtils.get(elements, 0);
        }
        return first;
    }

    /**
     * Gets the jpath for a '.' separated string defining the fully qualified path of a field in Json.
     * Array Indexes are referred via numbers.
     *
     * @param jpath the jpath
     * @return the fully qualified path as a JsonArray. Array Indexes are represented as numbers
     */
    public JsonArray getJpath(String jpath) {
        JsonArray jlist = new JsonArray();
        if (!StringUtils.contains(jpath, ".")) {
            jlist.add(getTypedPath(jpath));
        } else {
            String[] split = StringUtils.split(jpath, ".");
            for (String sp : split) {
                jlist.add(getTypedPath(sp));
            }
        }
        return jlist;
    }

    /**
     * Gets the jpath for a '.' separated string defining the fully qualified path of a field in Json.
     * Array Indexes are referred via numbers. If the input is anything apart from String,
     * JsonPrimitive String or JsonArray, it returns null
     *
     * @param jpath the jpath
     * @return the fully qualified path as a JsonArray. Array Indexes are represented as numbers
     */
    public JsonArray getJpath(JsonElement jpath) {
        JsonArray path = null;
        if (isArray(jpath)) {
            path = asJsonArray(jpath);
        } else if (isString(jpath)) {
            path = getJpath(asString(jpath));
        }
        return path;
    }

    /**
     * New json array.
     *
     * @param path the path
     * @return the json array
     */
    public JsonArray newJsonArray(Object... path) {
        JsonArray output = new JsonArray();
        for (Object p : path) {
            output.add(getTypedPath(p, Boolean.FALSE));
        }
        return output;
    }

    /**
     * Null safe toString method for a json element.
     *
     * @param source the element
     * @return the json string
     */
    public String toString(JsonElement source) {
        return null != source ? source.toString() : null;
    }

    /**
     * Deep clone.
     *
     * @param source the source json
     * @return the deep clone of the json
     */
    public JsonElement deepClone(JsonElement source) {
        return isNotNull(source) ? source.deepCopy() : source;
    }

    /**
     * Deep clone.
     *
     * @param source the source json
     * @return the deep clone of the json
     */
    public JsonObject deepClone(JsonObject source) {
        return isNotNull(source) ? source.deepCopy() : source;
    }

    /**
     * Deep clone.
     *
     * @param source the source json
     * @return the deep clone of the json
     */
    public JsonArray deepClone(JsonArray source) {
        return isNotNull(source) ? source.deepCopy() : source;
    }

    /**
     * Deep clone.
     *
     * @param source the source json
     * @return the deep clone of the json
     */
    public JsonPrimitive deepClone(JsonPrimitive source) {
        return isNotNull(source) ? source.deepCopy() : source;
    }

    /**
     * Gets the subset json from a source json.
     *
     * @param source the json source which can be Object, Array or a Primitive
     * @param jPathArray the array of jPaths defining the full qualified json paths to the required
     *     fields
     * @return the json subset
     */
    public JsonElement subset(JsonElement source, JsonArray jPathArray) {
        JsonElement finalValue = JsonNull.INSTANCE;
        if (jPathArray.size() == 0) {
            finalValue = source;
        } else {
            finalValue = new JsonObject();
            for (JsonElement jsonElement : jPathArray) {
                JsonElement subset = JsonNull.INSTANCE;
                if (isArray(jsonElement)) {
                    JsonArray pathArray = asJsonArray(jsonElement);
                    subset = get(source, pathArray);
                    finalValue = set(finalValue, pathArray, subset);
                } else if (isString(jsonElement)) {
                    String jpath = asString(jsonElement);
                    subset = get(source, jpath);
                    finalValue = set(finalValue, jpath, subset);
                } else {
                    finalValue = JsonNull.INSTANCE;
                    break;
                }
            }
        }
        return finalValue;
    }

    /**
     * Simple merge of two json objects. Json fields are not recursively merged
     *
     * @param to the json object to which the other json must be merged
     * @param from the json object which should be merged
     * @return the merged json object
     */
    public JsonObject simpleMerge(JsonObject to, JsonObject from) {
        from = asJsonObject(from, new JsonObject());
        to = asJsonObject(to, new JsonObject());
        for (Entry<String, JsonElement> entry : from.entrySet()) {
            to.add(entry.getKey(), entry.getValue());
        }
        return to;
    }

    /**
     * Simple merge of multiple json objects to a destination json.
     *
     * @param to the json object to which the other json must be merged
     * @param from the json objects which should be merged
     * @return the merged json object
     */
    public JsonObject simpleMerge(JsonObject to, JsonObject... from) {
        to = asJsonObject(to, new JsonObject());
        JsonObject finalTo = to;
        for (JsonObject jsonObject : from) {
            finalTo = simpleMerge(finalTo, jsonObject);
        }
        return finalTo;
    }

    /**
     * A recursive merge of two json elements.
     *
     * @param source1 the first json element
     * @param source2 the second json element
     * @return the recursively merged json element
     */
    public JsonElement merge(JsonElement source1, JsonElement source2) {
        return merge(source1, source2, Boolean.FALSE);
    }

    /**
     * A recursive merge of two json elements.
     *
     * @param source1 the first json element
     * @param source2 the second json element
     * @param mergeArray the flag to denote if arrays should be merged
     * @return the recursively merged json element
     */
    public JsonElement merge(JsonElement source1, JsonElement source2, Boolean mergeArray) {
        mergeArray = null == mergeArray ? Boolean.FALSE : mergeArray;
        JsonElement result = JsonNull.INSTANCE;
        source1 = asJsonElement(source1, JsonNull.INSTANCE);
        source2 = asJsonElement(source2, JsonNull.INSTANCE);
        if (source1.getClass().equals(source2.getClass())) {
            if (source1.isJsonObject()) {
                JsonObject obj1 = asJsonObject(source1);
                JsonObject obj2 = asJsonObject(source2);
                result = obj1;
                JsonObject resultObj = result.getAsJsonObject();
                for (Entry<String, JsonElement> entry : obj1.entrySet()) {
                    String key = entry.getKey();
                    JsonElement value1 = entry.getValue();
                    JsonElement value2 = obj2.get(key);
                    JsonElement merge = merge(value1, value2, mergeArray);
                    resultObj.add(key, merge);
                }
                for (Entry<String, JsonElement> entry : obj2.entrySet()) {
                    String key = entry.getKey();
                    if (!resultObj.has(key)) {
                        resultObj.add(key, entry.getValue());
                    }
                }
            } else if (source1.isJsonArray()) {
                result = new JsonArray();
                JsonArray resultArray = result.getAsJsonArray();
                JsonArray array1 = asJsonArray(source1);
                JsonArray array2 = asJsonArray(source2);
                int index = 0;
                int a1size = array1.size();
                int a2size = array2.size();

                if (!mergeArray) {
                    for (; index < a1size && index < a2size; index++) {
                        resultArray.add(merge(array1.get(index), array2.get(index), mergeArray));
                    }
                }

                for (; index < a1size; index++) {
                    resultArray.add(array1.get(index));
                }

                index = mergeArray ? 0 : index;

                for (; index < a2size; index++) {
                    resultArray.add(array2.get(index));
                }

            } else {
                result = source1 != null ? source1 : source2;
            }
        } else {
            result = isNotNull(source1) ? source1 : source2;
        }
        return result;
    }

    /**
     * Update result.
     *
     * @param result the result
     * @param field the field
     * @return the json element
     */
    protected JsonElement updateResult(JsonElement result, JsonElement field) {
        if (isNull(result)) {
            result = field;
        }
        return result;
    }

    /**
     * Sets the field.
     *
     * @param field the field
     * @param path the path
     * @param value the value
     * @return the json element
     */
    protected JsonElement setField(JsonElement field, JsonElement path, JsonElement value) {
        if (isNumber(path)) {
            JsonArray jArray = asJsonArray(field.getAsJsonArray(), new JsonArray());
            int index = path.getAsInt();
            repleteArray(jArray, index, JsonNull.class);
            jArray.set(index, value);
            field = jArray;
        } else {
            JsonObject jObject = asJsonObject(field, new JsonObject());
            jObject.add(path.getAsString(), value);
            field = jObject;
        }
        return field;
    }

    /**
     * Gets the typed path.
     *
     * @param p the p
     * @return the typed path
     */
    private JsonElement getTypedPath(Object p) {
        return getTypedPath(p, Boolean.TRUE);
    }

    /**
     * Gets the typed path.
     *
     * @param p the p
     * @param strToNum the str to num
     * @return the typed path
     */
    private JsonElement getTypedPath(Object p, Boolean strToNum) {
        JsonElement result = JsonNull.INSTANCE;
        if (p instanceof Number) {
            result = new JsonPrimitive((Number) p);
        } else if (p instanceof String) {
            String sp = StringUtils.trimToEmpty((String) p);
            result = strToNum ? (NumberUtils.isCreatable(sp) ? new JsonPrimitive(NumberUtils.toInt(sp))
                    : new JsonPrimitive(sp)) : new JsonPrimitive(sp);
        } else if (p instanceof Boolean) {
            result = new JsonPrimitive((Boolean) p);
        } else if (p instanceof Character) {
            result = new JsonPrimitive((Character) p);
        } else if (p instanceof JsonElement) {
            result = (JsonElement) p;
        }
        return result;
    }

    /**
     * Gets the replete field.
     *
     * @param field the field
     * @param path the path
     * @param nextPath the next path
     * @return the replete field
     */
    protected JsonElement getRepleteField(JsonElement field, JsonElement path, JsonElement nextPath) {
        if (isNumber(path)) {
            Integer index = path.getAsInt();
            if (!isArray(field)) {
                field = new JsonArray();
            }
            JsonArray fArray = repleteArray(field.getAsJsonArray(), index,
                    isNumber(nextPath) ? JsonArray.class : JsonObject.class);
            field = fArray;
        } else {
            String fieldName = path.getAsString();
            if (!isObject(field)) {
                field = new JsonObject();
            }
            JsonObject fJson = repleteJson(field.getAsJsonObject(), fieldName,
                    isNumber(nextPath) ? JsonArray.class : JsonObject.class);
            field = fJson;
        }
        return field;
    }

    /**
     * Replete array.
     *
     * @param source the source
     * @param index the index
     * @param type the type
     * @return the json array
     */
    private JsonArray repleteArray(JsonArray source, Integer index, Class<? extends JsonElement> type) {
        source = isNull(source) ? new JsonArray() : source;
        for (int i = 0; i <= index; i++) {
            try {
                source.get(i);
            } catch (IndexOutOfBoundsException e) {
                Class<? extends JsonElement> newType = JsonNull.class;
                if (i == index) {
                    newType = type;
                }
                source.add(getNewElement(newType));
            }
        }
        if (!source.get(index).getClass().equals(type)) {
            log.warn(String.format(
                    "The element at index %s in the source %s, does not match %s. Creating a new element.", index,
                    source, type));
            source.add(getNewElement(type));
        }
        return source;
    }

    /**
     * Replete json.
     *
     * @param source the source
     * @param fieldName the field name
     * @param type the type
     * @return the json object
     */
    private JsonObject repleteJson(JsonObject source, String fieldName, Class<? extends JsonElement> type) {
        source = isNull(source) ? new JsonObject() : source;
        JsonElement field = source.get(fieldName);
        if (isNull(field) || !field.getClass().equals(type)) {
            source.add(fieldName, getNewElement(type));
        }
        return source;
    }

    /**
     * Jsonify.
     *
     * @param source the source
     * @return the json element
     */
    public JsonElement jsonify(Object source) {
        JsonElement result = JsonNull.INSTANCE;
        if (source instanceof JsonElement) {
            result = (JsonElement) source;
        } else {
            try {
                result = GsonConvertor.getInstance().deserialize(source, JsonElement.class);

            } catch (ConvertorException e) {
                String msg = String.format("Could not deserialise object %s to json.", source);
                log.error(msg);
                log.debug(msg, e);
            }
        }
        return result;
    }

    /**
     * Gets the new element.
     *
     * @param type the type
     * @return the new element
     */
    private JsonElement getNewElement(Class<? extends JsonElement> type) {
        JsonElement element = JsonNull.INSTANCE;
        if (JsonObject.class.equals(type)) {
            element = new JsonObject();
        } else if (JsonArray.class.equals(type)) {
            element = new JsonArray();
        } else {
            try {
                element = type.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                log.error(String.format("Could not instantiate json element of type %s", type));
                log.debug(String.format("Could not instantiate json element of type %s.", type), e);
            }
        }
        return element;
    }
}