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

jodd.json.JsonObject Maven / Gradle / Ivy

There is a newer version: 5.1.0-20190624
Show newest version
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.json;

import jodd.util.collection.MapEntry;

import java.util.Base64;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;

/**
 * Representation of JSON object.
 * @see JsonArray
 */
public class JsonObject implements Iterable> {

	private Map map;

	/**
	 * Create a new, empty instance.
	 */
	public JsonObject() {
		map = new LinkedHashMap<>();
	}

	/**
	 * Create an instance from a Map. The Map is not copied.
	 */
	public JsonObject(final Map map) {
		this.map = map;
	}

	// ---------------------------------------------------------------- get

	/**
	 * Returns the string value with the specified key.
	 */
	public String getString(final String key) {
		CharSequence cs = (CharSequence) map.get(key);
		return cs == null ? null : cs.toString();
	}

	/**
	 * returns the integer value with the specified key.
	 */
	public Integer getInteger(final String key) {
		Number number = (Number) map.get(key);

		if (number == null) {
			return null;
		}
		if (number instanceof Integer) {
			return (Integer) number;
		}
		return number.intValue();
	}

	/**
	 * Returns the long value with the specified key.
	 */
	public Long getLong(final String key) {
		Number number = (Number) map.get(key);

		if (number == null) {
			return null;
		}
		if (number instanceof Long) {
			return (Long) number;
		}
		return number.longValue();
	}

	/**
	 * Returns the double value with the specified key.
	 */
	public Double getDouble(final String key) {
		Number number = (Number) map.get(key);

		if (number == null) {
			return null;
		}
		if (number instanceof Double) {
			return (Double) number;
		}
		return number.doubleValue();
	}

	/**
	 * Returns the float value with the specified key.
	 */
	public Float getFloat(final String key) {
		Number number = (Number) map.get(key);

		if (number == null) {
			return null;
		}
		if (number instanceof Float) {
			return (Float) number;
		}
		return number.floatValue();
	}

	/**
	 * Returns the boolean value with the specified key.
	 */
	public Boolean getBoolean(final String key) {
		return (Boolean) map.get(key);
	}

	/**
	 * Returns the {@code JsonObject} value with the specified key.
	 */
	public JsonObject getJsonObject(final String key) {
		Object val = map.get(key);

		if (val instanceof Map) {
			val = new JsonObject((Map) val);
		}
		return (JsonObject) val;
	}

	/**
	 * Returns the {@link JsonArray} value with the specified key
	 */
	public JsonArray getJsonArray(final String key) {
		Object val = map.get(key);

		if (val instanceof List) {
			val = new JsonArray((List) val);
		}
		return (JsonArray) val;
	}

	/**
	 * Returns the binary value with the specified key.
	 * 

* JSON itself has no notion of a binary. This extension complies to the RFC-7493. * THe byte array is Base64 encoded binary. */ public byte[] getBinary(final String key) { String encoded = (String) map.get(key); return encoded == null ? null : Base64.getDecoder().decode(encoded); } /** * Returns the value with the specified key, as an object. */ @SuppressWarnings("unchecked") public T getValue(final String key) { T val = (T) map.get(key); if (val instanceof Map) { return (T) new JsonObject((Map) val); } if (val instanceof List) { return (T) new JsonArray((List) val); } return val; } // ---------------------------------------------------------------- get + default /** * Like {@link #getString(String)} but specifies a default value to return if there is no entry. */ public String getString(final String key, final String def) { String val = getString(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } /** * Like {@link #getInteger(String)} but specifies a default value to return if there is no entry. */ public Integer getInteger(final String key, final Integer def) { Integer val = getInteger(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } /** * Like {@link #getLong(String)} but specifies a default value to return if there is no entry. */ public Long getLong(final String key, final Long def) { Long val = getLong(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } /** * Like {@link #getDouble(String)} but specifies a default value to return if there is no entry. */ public Double getDouble(final String key, final Double def) { Double val = getDouble(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } /** * Like {@link #getFloat(String)} but specifies a default value to return if there is no entry. */ public Float getFloat(final String key, final Float def) { Float val = getFloat(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } /** * Like {@link #getBoolean(String)} but specifies a default value to return if there is no entry. */ public Boolean getBoolean(final String key, final Boolean def) { Boolean val = getBoolean(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } /** * Like {@link #getJsonObject(String)} but specifies a default value to return if there is no entry. */ public JsonObject getJsonObject(final String key, final JsonObject def) { JsonObject val = getJsonObject(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } /** * Like {@link #getJsonArray(String)} but specifies a default value to return if there is no entry. */ public JsonArray getJsonArray(final String key, final JsonArray def) { JsonArray val = getJsonArray(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } /** * Like {@link #getBinary(String)} but specifies a default value to return if there is no entry. */ public byte[] getBinary(final String key, final byte[] def) { byte[] val = getBinary(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } /** * Like {@link #getValue(String)} but specifies a default value to return if there is no entry. */ public T getValue(final String key, final T def) { T val = getValue(key); if (val == null) { if (map.containsKey(key)) { return null; } return def; } return val; } // ---------------------------------------------------------------- misc /** * Returns {@code true} if the JSON object contain the specified key. */ public boolean containsKey(final String key) { return map.containsKey(key); } /** * Return the set of field names in the JSON objects. */ public Set fieldNames() { return map.keySet(); } // ---------------------------------------------------------------- put /** * Puts an Enum into the JSON object with the specified key. *

* JSON has no concept of encoding Enums, so the Enum will be converted to a String using the {@code java.lang.Enum#name} * method and the value put as a String. */ public JsonObject put(final String key, final Enum value) { Objects.requireNonNull(key); map.put(key, value == null ? null : value.name()); return this; } /** * Puts an {@code CharSequence} into the JSON object with the specified key. */ public JsonObject put(final String key, final CharSequence value) { Objects.requireNonNull(key); map.put(key, value == null ? null : value.toString()); return this; } /** * Puts a string into the JSON object with the specified key. */ public JsonObject put(final String key, final String value) { Objects.requireNonNull(key); map.put(key, value); return this; } /** * Puts an integer into the JSON object with the specified key. */ public JsonObject put(final String key, final Integer value) { Objects.requireNonNull(key); map.put(key, value); return this; } /** * Puts a long into the JSON object with the specified key. */ public JsonObject put(final String key, final Long value) { Objects.requireNonNull(key); map.put(key, value); return this; } /** * Puts a double into the JSON object with the specified key. */ public JsonObject put(final String key, final Double value) { Objects.requireNonNull(key); map.put(key, value); return this; } /** * Puts a float into the JSON object with the specified key. */ public JsonObject put(final String key, final Float value) { Objects.requireNonNull(key); map.put(key, value); return this; } /** * Puts a boolean into the JSON object with the specified key. */ public JsonObject put(final String key, final Boolean value) { Objects.requireNonNull(key); map.put(key, value); return this; } /** * Puts a {@code null} value into the JSON object with the specified key. */ public JsonObject putNull(final String key) { Objects.requireNonNull(key); map.put(key, null); return this; } /** * Puts another JSON object into the JSON object with the specified key. */ public JsonObject put(final String key, final JsonObject value) { Objects.requireNonNull(key); map.put(key, value); return this; } /** * Puts a {@link JsonArray} into the JSON object with the specified key. */ public JsonObject put(final String key, final JsonArray value) { Objects.requireNonNull(key); map.put(key, value); return this; } /** * Puts a {@code byte[]} into the JSON object with the specified key. *

* Follows JSON extension RFC7493, where binary will first be Base64 * encoded before being put as a String. */ public JsonObject put(final String key, final byte[] value) { Objects.requireNonNull(key); map.put(key, value == null ? null : Base64.getEncoder().encodeToString(value)); return this; } /** * Puts an object into the JSON object with the specified key. */ public JsonObject put(final String key, Object value) { Objects.requireNonNull(key); value = resolveValue(value); map.put(key, value); return this; } @SuppressWarnings("StatementWithEmptyBody") static Object resolveValue(Object value) { if (value == null) { // OK } else if (value instanceof Number) { // OK } else if (value instanceof Boolean) { // OK } else if (value instanceof String) { // OK } else if (value instanceof Character) { // OK } else if (value instanceof CharSequence) { value = value.toString(); } else if (value instanceof JsonObject) { // OK } else if (value instanceof JsonArray) { // OK } else if (value instanceof Map) { value = new JsonObject((Map) value); } else if (value instanceof List) { value = new JsonArray((List) value); } else if (value instanceof byte[]) { value = Base64.getEncoder().encodeToString((byte[]) value); } else { throw new JsonException("Illegal JSON type: " + value.getClass()); } return value; } // ---------------------------------------------------------------- remove /** * Removes an entry from this object. */ public Object remove(final String key) { return map.remove(key); } // ---------------------------------------------------------------- merge /** * Merges in another JSON object. *

* This is the equivalent of putting all the entries of the other JSON object into this object. This is not a deep * merge, entries containing (sub) JSON objects will be replaced entirely. */ public JsonObject mergeIn(final JsonObject other) { return mergeIn(other, 1); } /** * Merges in another JSON object. * A deep merge (recursive) matches (sub) JSON objects in the existing tree and replaces all * matching entries. JsonArrays are treated like any other entry, i.e. replaced entirely. */ public JsonObject mergeInDeep(final JsonObject other) { return mergeIn(other, Integer.MAX_VALUE); } /** * Merges in another JSON object. * The merge is deep (recursive) to the specified level. If depth is 0, no merge is performed, * if depth is greater than the depth of one of the objects, a full deep merge is performed. */ @SuppressWarnings("unchecked") public JsonObject mergeIn(final JsonObject other, final int depth) { if (depth < 1) { return this; } if (depth == 1) { map.putAll(other.map); return this; } for (Map.Entry e : other.map.entrySet()) { map.merge(e.getKey(), e.getValue(), (oldVal, newVal) -> { if (oldVal instanceof Map) { oldVal = new JsonObject((Map) oldVal); } if (newVal instanceof Map) { newVal = new JsonObject((Map) newVal); } if (oldVal instanceof JsonObject && newVal instanceof JsonObject) { return ((JsonObject) oldVal).mergeIn((JsonObject) newVal, depth - 1); } return newVal; }); } return this; } // ---------------------------------------------------------------- encode /** * Returns the underlying {@code Map} as is. */ public Map map() { return map; } /** * Returns a stream of the entries in the JSON object. */ public Stream> stream() { return map.entrySet().stream(); } /** * Returns an iterator of the entries in the JSON object. */ @Override public Iterator> iterator() { return new Iter(map.entrySet().iterator()); } /** * Returns the number of entries in the JSON object. */ public int size() { return map.size(); } /** * Removes all the entries in this JSON object. */ public JsonObject clear() { map.clear(); return this; } /** * Returns {@code true} if JSON object is empty. */ public boolean isEmpty() { return map.isEmpty(); } /** * Returns JSON serialized by default {@link JsonSerializer}. */ @Override public String toString() { return JsonSerializer.create().deep(true).serialize(map); } // ---------------------------------------------------------------- equals/hash @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } return objectEquals(map, o); } static boolean objectEquals(final Map m1, final Object o2) { Map m2; if (o2 instanceof JsonObject) { m2 = ((JsonObject) o2).map; } else if (o2 instanceof Map) { m2 = (Map) o2; } else { return false; } if (m1.size() != m2.size()) { return false; } for (Map.Entry entry : m1.entrySet()) { Object val = entry.getValue(); if (val == null) { if (m2.get(entry.getKey()) != null) { return false; } } else { if (!elementEquals(entry.getValue(), m2.get(entry.getKey()))) { return false; } } } return true; } static boolean elementEquals(final Object o1, final Object o2) { if (o1 == o2) { return true; } if (o1 instanceof JsonObject) { return objectEquals(((JsonObject) o1).map, o2); } if (o1 instanceof Map) { return objectEquals((Map) o1, o2); } if (o1 instanceof JsonArray) { return JsonArray.arrayEquals(((JsonArray) o1).list(), o2); } if (o1 instanceof List) { return JsonArray.arrayEquals((List) o1, o2); } if (o1 instanceof Number && o2 instanceof Number && o1.getClass() != o2.getClass()) { Number n1 = (Number) o1; Number n2 = (Number) o2; if (o1 instanceof Float || o1 instanceof Double || o2 instanceof Float || o2 instanceof Double) { return n1.doubleValue() == n2.doubleValue(); } else { return n1.longValue() == n2.longValue(); } } return o1.equals(o2); } @Override public int hashCode() { return map.hashCode(); } // ---------------------------------------------------------------- iterator /** * Iterator over values that handles maps and lists and wraps them in the JSON objects. */ private class Iter implements Iterator> { final Iterator> mapIterator; Iter(final Iterator> mapIterator) { this.mapIterator = mapIterator; } @Override public boolean hasNext() { return mapIterator.hasNext(); } @Override public Map.Entry next() { Map.Entry entry = mapIterator.next(); if (entry.getValue() instanceof Map) { return MapEntry.createUnmodifiable(entry.getKey(), new JsonObject((Map) entry.getValue())); } if (entry.getValue() instanceof List) { return MapEntry.createUnmodifiable(entry.getKey(), new JsonArray((List) entry.getValue())); } return entry; } @Override public void remove() { mapIterator.remove(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy