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

com.fasterxml.jackson.contrib.jsonpath.util.CandidateField Maven / Gradle / Ivy

Go to download

Provides concise annotation based unmarshalling of Json using JsonPath and Jackson

The newest version!
package com.fasterxml.jackson.contrib.jsonpath.util;

import static java.lang.String.format;

import java.lang.reflect.Field;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.contrib.jsonpath.annotation.JsonPath;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Wraps given field and its annotations (if any) into a more convenient structure.
 * The class also provides additional set of methods for resoloving annotation values
 * as proper Json paths
 */
public class CandidateField {

	private static final Logger LOG = LoggerFactory.getLogger(CandidateField.class);
	private final ReflectionUtil reflectionUtil;
	private final JsonProperty jsonPropertyAnnotation;
	private final JsonPath jsonPathAnnotation;
	private final Field field;
	private final Object containingObject;

	/**
	 * Custom constructor that wraps field and its annotations (if any).
	 *
	 * @param field field to wrapped
	 */
	public CandidateField(final Field field, final Object containingObject) {
		this.containingObject = containingObject;
		this.field = field;
		this.jsonPathAnnotation = field.getAnnotation(JsonPath.class);
		this.jsonPropertyAnnotation = field.getAnnotation(JsonProperty.class);
		sanityCheck();
		this.reflectionUtil = new ReflectionUtil();
	}

	/**
	 * Get field value from a given object.
	 *
	 * @param resultObject object from which value will be taken
	 * @return field value
	 * @throws IllegalAccessException reflection exception
	 */
	public Object getFieldValue(final Object resultObject) throws IllegalAccessException {
		return reflectionUtil.getFieldValue(resultObject, this.field);
	}

	public Field getField() {
		return field;
	}

	public JsonProperty getJsonPropertyAnnotation() {
		return jsonPropertyAnnotation;
	}

	public JsonPath getJsonPathAnnotation() {
		return jsonPathAnnotation;
	}

	/**
	 * Get annotation value from either annotation, if exists.
	 * In case that Json annotation is not present, field name will be used on the same way
	 * like the field is annotated with JsonProperty.
	 *
	 * @return Json path from either annotation or field name, if annotation is missing.
	 */
	public String getJsonPathFromField() {
		return getJsonAnnotationValue(false, false);
	}

	/**
	 * Get annotation value and transform JsonProperty value to a relative JsonPath, if required.
	 * In case that either of Json annotations is not present, field name will be used on the same way
	 * like the field is annotated with JsonProperty.
	 *
	 * @param isAbsolutePath if true, @JsonProperty value will be resoloved as relative; otherwise as absolute
	 * @return Json path
	 */
	public String getJsonAnnotationValue(final boolean isAbsolutePath) {
		return getJsonAnnotationValue(true, isAbsolutePath);
	}

	private String getJsonAnnotationValue(final boolean shouldResolveJsonPropertyAsJsonPath, final boolean isAbsolutePath) {

		if (jsonPathAnnotation != null) {
			return jsonPathAnnotation.value();
		}

		String jsonPath;
		if (jsonPropertyAnnotation == null) {
			jsonPath = field.getName();
		} else {
			jsonPath = jsonPropertyAnnotation.value();
		}

		if (shouldResolveJsonPropertyAsJsonPath) {
			return resolveJsonPropertyAsJsonPath(jsonPath, isAbsolutePath);
		}

		return jsonPath;
	}

	private String resolveJsonPropertyAsJsonPath(final String jsonPath, final boolean isAbsolutePath) {

		if (isAbsolutePath) {
			return jsonPath; //Jway ReadContext will resolve JsonProperty value as absolute path, by adding $.
		}

		//this makes JsonProperty annot value relative to parent JsonPath (or any other path)
		return "@." + jsonPath;
	}

	/*
	 * Ensure that field cannot have both JsonPath and JsonProperty annotations
	 */
	private void sanityCheck() {

		if (areConflictingAnnotationsPresent()) {
			String errorMessage = format("JsonProperty and JsonPath annotations both detected on field [%s] in class [%s]",
					getField().getName(), containingObject.getClass().getName());

			LOG.error(errorMessage);
			throw new IllegalStateException(errorMessage);
		}
	}

	/*
	 * Check if both @JsonPath and @JsonProperty are present.
	 * @return true if both annotations are present.
	 */
	private boolean areConflictingAnnotationsPresent() {
		return jsonPropertyAnnotation != null &&  jsonPathAnnotation != null;
	}

	/*
	 *  Rules to perform Json unmarshalling:
	 *  1. Field must be annotated with JsonPath or
	 *  2. Field is annotated with JsonProperty; it is primitive or (non-primitive and null)
	 *  3. if both annotations are missing, then check if field is non-primitive and null
	 *
	 * Note:
	 *		getFieldValue(resultObject,field) can't be resolved into var because in very first loop, returned value is null
	 *		while after performing unmarshalling may be non-null
	 */
	public boolean isAppropriateForJsonPathUnmarshalling()
			throws IllegalAccessException {

		final boolean isFieldPrimitive = reflectionUtil.isFieldPrimitive(getField());
		final Object fieldValue = getFieldValue(containingObject);

		final boolean shouldUnmarshallJsonPathAnnotation = getJsonPathAnnotation() != null;

		return shouldUnmarshallJsonPathAnnotation
				|| shouldUnmarshallJsonPropertyAnnotation(getJsonPropertyAnnotation(), isFieldPrimitive, fieldValue)
				|| shouldUnmarshallNonAnnotatedField(isFieldPrimitive, fieldValue);
	}

	private boolean shouldUnmarshallJsonPropertyAnnotation(final JsonProperty jsonPropertyAnnotation, final boolean isFieldPrimitive,
			final Object fieldValue) {

		return jsonPropertyAnnotation != null && shouldUnmarshallNonAnnotatedField(isFieldPrimitive, fieldValue);
	}

	private boolean shouldUnmarshallNonAnnotatedField(final boolean isFieldPrimitive, final Object fieldValue) {

		return isFieldPrimitive || fieldValue == null;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy