DEV Community

Angela
Angela

Posted on

How to automatically inject a validated property's name in the bean validation message

A project I've worked on in the past required us to inject the property path in validation error messages. To prevent repetitive work, I've written a rough MessageInterpolator implementation to add the property path as an expression variable:

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.internal.engine.MessageInterpolatorContext;

import javax.validation.MessageInterpolator;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;

/**
 * <p>Allows interpolation of messages with a {@code ${propertyPath} expression variable},
 * which will resolve to the property path of the value being validated.</p>
 */
@Slf4j
@RequiredArgsConstructor
public class PropertyPathAddingMessageInterpolator implements MessageInterpolator {

    private final MessageInterpolator delegateInterpolator;

    @Override
    public String interpolate(String messageTemplate, Context context) {
        Optional<Context> replacementContext = replacementContextFrom(context);
        return delegateInterpolator.interpolate(messageTemplate, replacementContext.orElse(context));
    }

    @Override
    public String interpolate(String messageTemplate, Context context, Locale locale) {
        Optional<Context> replacementContext = replacementContextFrom(context);
        return delegateInterpolator.interpolate(messageTemplate, replacementContext.orElse(context), locale);
    }

    private Optional<Context> replacementContextFrom(Context context) {
        if (!(context instanceof MessageInterpolatorContext)) {
            return Optional.empty();
        }
        MessageInterpolatorContext interpolatorContext = (MessageInterpolatorContext) context;
        Map<String, Object> expressionVariables = new HashMap<>(interpolatorContext.getExpressionVariables());
        expressionVariables.put("propertyPath", interpolatorContext.getPropertyPath());

        Context replacementContext = new MessageInterpolatorContext(
            interpolatorContext.getConstraintDescriptor(),
            interpolatorContext.getValidatedValue(),
            interpolatorContext.getRootBeanType(),
            interpolatorContext.getPropertyPath(),
            interpolatorContext.getMessageParameters(),
            expressionVariables,
            interpolatorContext.getExpressionLanguageFeatureLevel(),
            interpolatorContext.isCustomViolation()
        );
        return Optional.of(replacementContext);
    }

}
Enter fullscreen mode Exit fullscreen mode

This allows us to automatically populate ${propertyPath} in validation messages such as the following:

javax.validation.constraints.NotNull.message=${propertyPath} must not be null
Enter fullscreen mode Exit fullscreen mode

You must configure your ValidatorFactory to use this MessageInterpolator.

Top comments (0)