Dynamic Property Access and Mutation in Java Using Reflection and JSON Serialization

When building flexible, configuration-driven applications—such as rule engines, dynamic form processors, or metadata-driven reporting systems—it's often necessary to read from or write to object fields by name at runtime. This requirement arises when field identifiers are stored externally (e.g., in a database or configuration file) and cannnot be hard-coded.

The following implementation provides a lightweight, robust approach using standard Java reflection for direct field manipulation and com.fasterxml.jackson.databind.ObjectMapper (a more portable and secure alternative to FastJSON) for generic property extraction via serialization/deserialization.

Core Utility: Generic Property Extraction

public class PropertyAccessor { private static final ObjectMapper mapper = new ObjectMapper();

/**
 * Serializes an object to a JsonNode and retrieves the string value of a field.
 * Returns empty string if field is missing or null.
 */
public static String getStringValue(Object obj, String fieldName) {
    if (obj == null || fieldName == null) return "";

    try {
        JsonNode node = mapper.valueToTree(obj);
        JsonNode target = node.get(fieldName);
        return (target != null && !target.isNull()) ? target.asText() : "";
    } catch (Exception e) {
        // Log error in production (e.g., SLF4J)
        return "";
    }
}

/**
 * Safely sets a field value using reflection.
 * Supports private fields and basic type coercion (String → primitive wrappers).
 */
public static boolean setFieldValue(Object instance, String fieldName, Object value) {
    if (instance == null || fieldName == null) return false;

    try {
        Class> clazz = instance.getClass();
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);

        Class> targetType = field.getType();
        Object coercedValue = coerceValue(value, targetType);
        field.set(instance, coercedValue);
        return true;
    } catch (NoSuchFieldException | IllegalAccessException e) {
        return false;
    }
}

private static Object coerceValue(Object input, Class> targetType) {
    if (input == null || targetType.isInstance(input)) return input;

    if (targetType == Integer.class || targetType == int.class) {
        return input instanceof Number ? ((Number) input).intValue() : Integer.parseInt(input.toString());
    }
    if (targetType == Boolean.class || targetType == boolean.class) {
        return Boolean.parseBoolean(input.toString());
    }
    if (targetType == Double.class || targetType == double.class) {
        return input instanceof Number ? ((Number) input).doubleValue() : Double.parseDouble(input.toString());
    }
    return input.toString();
}

}


</div>### Configuration-Aware Resolver

This resolver interprets dot-notation paths (e.g., `user.profile.name`) and supports conditional expressions using a simple ternary syntax (`status=="active"?1:0`). It delegates actual field access to `PropertyAccessor`.

<div>```
import java.util.*;
import java.util.stream.Collectors;

public class ConfigurablePropertyResolver {

    /**
     * Resolves a property expression like "User.name" or "status==\"ON\"?1:0"
     * @param context Object serving as the evaluation context
     * @param expression Expression string (may contain dots or ternary logic)
     * @return Evaluated string result
     */
    public static String resolve(Object context, String expression) {
        if (expression == null || context == null) return "";

        // Handle ternary: status=="ON"?1:0
        if (expression.contains("?") && expression.contains(":")) {
            int qIndex = expression.indexOf('?');
            int cIndex = expression.indexOf(':');
            if (qIndex > 0 && cIndex > qIndex) {
                String conditionPart = expression.substring(0, qIndex).trim();
                String truePart = expression.substring(qIndex + 1, cIndex).trim();
                String falsePart = expression.substring(cIndex + 1).trim();

                if (conditionPart.contains("==")) {
                    String[] parts = conditionPart.split("==", 2);
                    String field = parts[0].trim();
                    String expected = parts[1].trim().replaceAll("[\"'\\s]", "");
                    String actual = PropertyAccessor.getStringValue(context, field);
                    return Objects.equals(actual, expected) ? truePart : falsePart;
                }
            }
        }

        // Handle dot-chained field access (e.g., "address.city")
        String[] segments = expression.split("\\.");
        Object current = context;
        for (String segment : segments) {
            if (current == null) break;
            current = PropertyAccessor.getStringValue(current, segment);
            if (segment.equals(segments[segments.length - 1])) {
                return (String) current;
            }
        }
        return "";
    }

    /**
     * Binds multiple expressions separated by semicolons into a concatenated result.
     * Useful for composite display labels or derived keys.
     */
    public static String resolveMultiple(Object context, String expressions) {
        if (expressions == null) return "";
        return Arrays.stream(expressions.split(";"))
                .map(expr -> resolve(context, expr.trim()))
                .filter(Objects::nonNull)
                .collect(Collectors.joining(""));
    }
}
    // Read field by name
    String name = PropertyAccessor.getStringValue(person, "name"); // "Alice"

    // Write field by name
    PropertyAccessor.setFieldValue(person, "age", "33");

    // Resolve conditional expression
    String statusLabel = ConfigurablePropertyResolver.resolve(person, "name==\"Alice\"?\"VIP\":\"USER\""); // "VIP"

    // Resolve chained path (if Person has an Address field)
    // String city = ConfigurablePropertyResolver.resolve(person, "address.city");

    // Resolve multiple expressions
    String summary = ConfigurablePropertyResolver.resolveMultiple(person,
            "name; ;age; ;status==\"active\"?\"✓\":\"✗\""); // "Alice 33 ✗"
}

}


</div>

Tags: java-reflection jackson-databind property-access dynamic-configuration ternary-expression

Posted on Sun, 25 Oct 2026 16:13:35 +0000 by Simmo