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

jodd.json.JsonContext 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.introspector.ClassDescriptor;
import jodd.introspector.ClassIntrospector;
import jodd.util.ClassUtil;
import jodd.util.Wildcard;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import static jodd.util.StringPool.NULL;

/**
 * JSON context used during serialization for building the JSON string.
 */
public class JsonContext extends JsonWriter {

	// ---------------------------------------------------------------- ctor

	protected final JsonSerializer jsonSerializer;
	protected final List bag;
	protected int bagSize = 0;
	protected final Path path;
	protected final boolean excludeNulls;
	protected final boolean excludeEmpty;
	protected final Function serializerResolver;

	public JsonContext(final JsonSerializer jsonSerializer, final Appendable appendable) {
		super(appendable, jsonSerializer.strictStringEncoding);
		this.jsonSerializer = jsonSerializer;
		this.bag = new ArrayList<>();
		this.path = new Path();
		this.excludeNulls = jsonSerializer.excludeNulls;
		this.excludeEmpty = jsonSerializer.excludeEmpty;
		this.serializerResolver = jsonSerializer.serializerResolver;
	}

	/**
	 * Returns {@link jodd.json.JsonSerializer}.
	 */
	public JsonSerializer getJsonSerializer() {
		return jsonSerializer;
	}

	/**
	 * Returns true if null values have to be excluded.
	 */
	public boolean isExcludeNulls() {
		return excludeNulls;
	}

	public boolean isExcludeEmpty() {
		return excludeEmpty;
	}

	// ---------------------------------------------------------------- path and value context

	protected JsonValueContext lastValueContext = null;

	/**
	 * Returns true if object has been already processed during the serialization.
	 * Used to prevent circular dependencies. Objects are matched by identity.
	 */
	public boolean pushValue(final Object value) {
		for (int i = 0; i < bagSize; i++) {
			JsonValueContext valueContext = bag.get(i);
			if (valueContext.getValue() == value) {
				return true;
			}
		}

		if (bagSize == bag.size()) {
			lastValueContext = new JsonValueContext(value);
			bag.add(lastValueContext);
		}
		else {
			lastValueContext = bag.get(bagSize);
			lastValueContext.reuse(value);
		}

		bagSize++;

		return false;
	}

	/**
	 * Removes object from current bag, indicating it is not anymore in the path.
	 */
	public void popValue() {
		bagSize--;
		if (bagSize == 0) {
			lastValueContext = null;
		} else {
			lastValueContext = bag.get(bagSize - 1);
		}
	}

	/**
	 * Returns current {@link jodd.json.JsonValueContext value context}.
	 * It may be null if value is not {@link #pushValue(Object) pushed} yet.
	 */
	public JsonValueContext peekValueContext() {
		return lastValueContext;
	}

	/**
	 * Returns current path.
	 */
	public Path getPath() {
		return path;
	}

	// ---------------------------------------------------------------- overwrite

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void pushName(final String name, final boolean withComma) {
		JsonValueContext valueContext = peekValueContext();

		if (valueContext != null) {
			valueContext.setPropertyName(name);
		}

		super.pushName(name, withComma);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void writeComma() {
		JsonValueContext valueContext = peekValueContext();

		if (valueContext != null) {
			valueContext.incrementIndex();
		}

		super.writeComma();
	}

	// ---------------------------------------------------------------- serializer

	/**
	 * Serializes the object using {@link jodd.json.TypeJsonSerializer type serializer}.
	 * Returns true if object was written, otherwise returns false.
	 */
	public boolean serialize(final Object object) {
		if (object == null) {
			write(NULL);

			return true;
		}

		TypeJsonSerializer typeJsonSerializer = null;

		// callback

		if (serializerResolver != null) {
			typeJsonSerializer = serializerResolver.apply(object);
		}

		if (typeJsonSerializer == null) {

			// + read paths map

			if (jsonSerializer.pathSerializersMap != null) {
				typeJsonSerializer = jsonSerializer.pathSerializersMap.get(path);
			}

			final Class type = object.getClass();

			// + read local types map

			if (jsonSerializer.typeSerializersMap != null) {
				typeJsonSerializer = jsonSerializer.typeSerializersMap.lookup(type);
			}

			// + globals

			if (typeJsonSerializer == null) {
				typeJsonSerializer = TypeJsonSerializerMap.get().lookup(type);
			}
		}

		return typeJsonSerializer.serialize(this, object);
	}

	// ---------------------------------------------------------------- matchers

	/**
	 * Matches property types that are ignored by default.
	 */
	public boolean matchIgnoredPropertyTypes(final Class propertyType, final boolean excludeMaps, final boolean include) {
		if (!include) {
			return false;
		}

		if (propertyType != null) {
			if (!jsonSerializer.deep) {
				ClassDescriptor propertyTypeClassDescriptor = ClassIntrospector.get().lookup(propertyType);

				if (propertyTypeClassDescriptor.isArray()) {
					return false;
				}
				if (propertyTypeClassDescriptor.isCollection()) {
					return false;
				}
				if (excludeMaps) {
					if (propertyTypeClassDescriptor.isMap()) {
						return false;
					}
				}
			}

			// still not excluded, continue with excluded types and type names

			// + excluded types

			if (jsonSerializer.excludedTypes != null) {
				for (Class excludedType : jsonSerializer.excludedTypes) {
					if (ClassUtil.isTypeOf(propertyType, excludedType)) {
						return false;
					}
				}
			}

			// + exclude type names

			final String propertyTypeName = propertyType.getName();

			if (jsonSerializer.excludedTypeNames != null) {
				for (String excludedTypeName : jsonSerializer.excludedTypeNames) {
					if (Wildcard.match(propertyTypeName, excludedTypeName)) {
						return false;
					}
				}
			}
		}

		return true;
	}

	/**
	 * Matched current path to queries. If match is found, provided include
	 * value may be changed.
	 */
	public boolean matchPathToQueries(final boolean include) {
		return jsonSerializer.rules.apply(path, include);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy