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

io.micronaut.json.tree.JsonNode Maven / Gradle / Ivy

There is a newer version: 4.7.5
Show newest version
/*
 * Copyright 2017-2021 original authors
 *
 * 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
 *
 * https://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 io.micronaut.json.tree;

import io.micronaut.core.annotation.Experimental;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Immutable class representing a json node. Json nodes can be either scalar (string, number, boolean, null) or
 * containers (object, array).
 *
 * @author Jonas Konrad
 * @since 3.1
 */
@Experimental
public abstract class JsonNode {

    JsonNode() {
    }

    /**
     * Create a new {@link JsonNode} representing this value.
     *
     * @param value to be converted to {@link JsonNode}
     * @return The {@link JsonNode} representing this value
     * @since 4.0.0
     */
    @NonNull
    public static JsonNode from(Object value) {
        if (value == null) {
            return JsonNull.INSTANCE;
        }
        if (value instanceof String s) {
            return createStringNode(s);
        }
        if (value instanceof Number n) {
            return createNumberNodeImpl(n);
        }
        if (value instanceof Boolean b) {
            return createBooleanNode(b);
        }
        if (value instanceof List list) {
            return createArrayNode(list.stream().map(JsonNode::from).toList());
        }
        if (value instanceof Map map) {
            var newMap = CollectionUtils.newLinkedHashMap(map.size());
            for (Map.Entry e : map.entrySet()) {
                Object key = e.getKey();
                if (key instanceof String s) {
                    newMap.put(s, from(e.getValue()));
                } else {
                    throw new IllegalStateException("Expected a String as a key in the map!");
                }
            }
            return createObjectNode(newMap);
        }
        throw new IllegalStateException("Unrecognized value: " + value);
    }

    /**
     * @return The singleton node representing {@code null}.
     */
    @NonNull
    public static JsonNode nullNode() {
        return JsonNull.INSTANCE;
    }

    /**
     * @param nodes The nodes in this array. Must not be modified after this method is called.
     * @return The immutable array node.
     */
    @NonNull
    public static JsonNode createArrayNode(@NonNull List nodes) {
        Objects.requireNonNull(nodes, "nodes");
        return new JsonArray(nodes);
    }

    /**
     * @param nodes The nodes in this object. Must not be modified after this method is called.
     * @return The immutable array node.
     */
    @NonNull
    public static JsonNode createObjectNode(Map nodes) {
        Objects.requireNonNull(nodes, "nodes");
        return new JsonObject(nodes);
    }

    /**
     * @param value The value of the node.
     * @return A json node representing the given boolean value.
     */
    @NonNull
    public static JsonNode createBooleanNode(boolean value) {
        return JsonBoolean.valueOf(value);
    }

    /**
     * @param value The value of the node.
     * @return A json node representing the given string value.
     */
    @NonNull
    public static JsonNode createStringNode(@NonNull String value) {
        Objects.requireNonNull(value, "value");
        return new JsonString(value);
    }

    /**
     * Hidden, so that we don't have to check that the number type is supported.
     *
     * @param value The raw numeric value.
     * @return The number node.
     */
    @Internal
    public static JsonNode createNumberNodeImpl(Number value) {
        Objects.requireNonNull(value, "value");
        return new JsonNumber(value);
    }

    /**
     * @param value The value of the node.
     * @return A json node representing the given numeric value.
     */
    @NonNull
    public static JsonNode createNumberNode(int value) {
        return createNumberNodeImpl(value);
    }

    /**
     * @param value The value of the node.
     * @return A json node representing the given numeric value.
     */
    @NonNull
    public static JsonNode createNumberNode(long value) {
        return createNumberNodeImpl(value);
    }

    /**
     * @param value The value of the node.
     * @return A json node representing the given numeric value.
     */
    @NonNull
    public static JsonNode createNumberNode(@NonNull BigDecimal value) {
        return createNumberNodeImpl(value);
    }

    /**
     * @param value The value of the node.
     * @return A json node representing the given numeric value.
     */
    @NonNull
    public static JsonNode createNumberNode(float value) {
        return createNumberNodeImpl(value);
    }

    /**
     * @param value The value of the node.
     * @return A json node representing the given numeric value.
     */
    @NonNull
    public static JsonNode createNumberNode(double value) {
        return createNumberNodeImpl(value);
    }

    /**
     * @param value The value of the node.
     * @return A json node representing the given numeric value.
     */
    @NonNull
    public static JsonNode createNumberNode(@NonNull BigInteger value) {
        return createNumberNodeImpl(value);
    }

    /**
     * Get the value reprinting this node.
     *
     * @return The value of the node
     * @since 4.0.0
     */
    @Nullable
    public abstract Object getValue();

    /**
     * @return {@code true} iff this is a number node.
     */
    public boolean isNumber() {
        return false;
    }

    /**
     * @return The raw numeric value of this node. Always full precision.
     * @throws IllegalStateException if this is not a number node.
     */
    @NonNull
    public Number getNumberValue() {
        throw new IllegalStateException("Not a number");
    }

    /**
     * @return The value of this number node, converted to {@code int}. May lose precision.
     * @throws IllegalStateException if this is not a number node.
     */
    public final int getIntValue() {
        return getNumberValue().intValue();
    }

    /**
     * @return The value of this number node, converted to {@code long}. May lose precision.
     * @throws IllegalStateException if this is not a number node.
     */
    public final long getLongValue() {
        return getNumberValue().longValue();
    }

    /**
     * @return The value of this number node, converted to {@code float}. May lose precision.
     * @throws IllegalStateException if this is not a number node.
     */
    public final float getFloatValue() {
        return getNumberValue().floatValue();
    }

    /**
     * @return The value of this number node, converted to {@code double}. May lose precision.
     * @throws IllegalStateException if this is not a number node.
     */
    public final double getDoubleValue() {
        return getNumberValue().doubleValue();
    }

    /**
     * @return The value of this number node, converted to {@link BigInteger}. May lose the decimal part.
     * @throws IllegalStateException if this is not a number node.
     */
    @NonNull
    public final BigInteger getBigIntegerValue() {
        Number numberValue = getNumberValue();
        if (numberValue instanceof BigInteger integer) {
            return integer;
        } else if (numberValue instanceof BigDecimal decimal) {
            return decimal.toBigInteger();
        } else {
            return BigInteger.valueOf(numberValue.longValue());
        }
    }

    /**
     * @return The value of this number node, converted to {@link BigDecimal}.
     * @throws IllegalStateException if this is not a number node.
     */
    @NonNull
    public final BigDecimal getBigDecimalValue() {
        Number numberValue = getNumberValue();
        if (numberValue instanceof BigInteger integer) {
            return new BigDecimal(integer);
        } else if (numberValue instanceof BigDecimal decimal) {
            return decimal;
        } else if (numberValue instanceof Long) {
            return BigDecimal.valueOf(numberValue.longValue());
        } else {
            // all other types, including the int types, fit into double
            return BigDecimal.valueOf(numberValue.doubleValue());
        }
    }

    /**
     * @return {@code true} iff this is a string node.
     */
    public boolean isString() {
        return false;
    }

    /**
     * @return The value of this string node.
     * @throws IllegalStateException if this is not a string node.
     */
    @NonNull
    public String getStringValue() {
        throw new IllegalStateException("Not a string");
    }

    /**
     * Attempt to coerce this node to a string.
     *
     * @return The coerced string value.
     * @throws IllegalStateException if this node is not a scalar value
     */
    @NonNull
    public String coerceStringValue() {
        throw new IllegalStateException("Not a scalar value");
    }

    /**
     * @return {@code true} iff this is a boolean node.
     */
    public boolean isBoolean() {
        return false;
    }

    /**
     * @return The value of this boolean node.
     * @throws IllegalStateException if this is not a boolean node.
     */
    public boolean getBooleanValue() {
        throw new IllegalStateException("Not a boolean");
    }

    /**
     * @return {@code true} iff this is the null node.
     */
    public boolean isNull() {
        return false;
    }

    /**
     * @return The number of immediate children of this node, or {@code 0} if this is not a container node.
     */
    public abstract int size();

    /**
     * @return An {@link Iterable} of all values of this array or object node.
     * @throws IllegalStateException if this is not a container node.
     */
    @NonNull
    public abstract Iterable values();

    /**
     * @return An {@link Iterable} of all entries of this object node.
     * @throws IllegalStateException if this is not an object node.
     */
    @NonNull
    public abstract Iterable> entries();

    /**
     * @return {@code true} iff this node is a value node (string, number, boolean, null).
     */
    public boolean isValueNode() {
        return false;
    }

    /**
     * @return {@code true} iff this node is a container node (array or object).
     */
    public boolean isContainerNode() {
        return false;
    }

    /**
     * @return {@code true} iff this node is an array node.
     */
    public boolean isArray() {
        return false;
    }

    /**
     * @return {@code true} iff this node is an object node.
     */
    public boolean isObject() {
        return false;
    }

    /**
     * @param fieldName The field name.
     * @return The field with the given name, or {@code null} if there is no such field or this is not an object.
     */
    @Nullable
    public abstract JsonNode get(@NonNull String fieldName);

    /**
     * @param index The index into this array.
     * @return The field at the given index, or {@code null} if there is no such field or this is not an array.
     */
    @Nullable
    public abstract JsonNode get(int index);
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy