Custom Enum and JSON Parameter Binding in Spring MVC

This article explains how to handle enum and JSON string parameters in Spring MVC by creating custom annotations and argument resolvers, demonstrates the implementation steps with full code examples, and also reviews Spring MVC's built‑in parameter binding mechanisms such as @RequestParam, @PathVariable, @RequestBody, and @ModelAttribute.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Custom Enum and JSON Parameter Binding in Spring MVC

Spring MVC provides many convenient ways to bind request parameters, but handling enums or JSON strings often requires custom solutions. This article shares a complete approach for both cases.

Enum Parameter Binding

Simple enums (e.g., one, two) can be bound directly. However, when the request supplies numeric values (0, 1) for an enum like StatusEnum, Spring cannot bind automatically and throws an error.

public enum StatusEnum {
    online(1),
    offline(0);

    private Integer value;

    StatusEnum(Integer value) {
        this.value = value;
    }

    public Integer getValue() {
        return value;
    }
}

To solve this, we define a custom annotation @EnumParam and a corresponding argument resolver.

Define Annotation

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnumParam {
    String value() default "";
    /**
     * Method to invoke on the enum for value conversion.
     * If empty, the default <code>name()</code> method is used.
     */
    String valueMethod() default "";
}

The value() attribute maps the request parameter name, while valueMethod() specifies which enum method should be called (e.g., getValue()).

Define Enum Argument Resolver

public class EnumParamArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(EnumParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        EnumParam annotation = parameter.getParameterAnnotation(EnumParam.class);
        Object object = null;
        if (annotation != null) {
            String parameterName = annotation.value();
            if (!StringUtils.hasText(parameterName)) {
                parameterName = annotation.name();
            }
            if (!StringUtils.hasText(parameterName)) {
                parameterName = parameter.getParameterName();
            }
            String value = webRequest.getParameter(parameterName);
            if (StringUtils.hasText(value)) {
                Class<?> objectType = parameter.getParameterType();
                String method = Objects.equals(annotation.valueMethod(), "") ? "valueOf" : annotation.valueMethod();
                Object[] enumConstants = objectType.getEnumConstants();
                Method declaredMethod = objectType.getDeclaredMethod(method);
                try {
                    for (Object enumConstant : enumConstants) {
                        Object invoke = method.equals("valueOf") ? declaredMethod.invoke(enumConstant, enumConstant.toString())
                                                               : declaredMethod.invoke(enumConstant);
                        if (invoke != null && Convert.toStr(invoke).equals(Convert.toStr(value))) {
                            object = enumConstant;
                            break;
                        }
                    }
                } catch (Exception e) {
                    log.error("Parameter enum conversion failed: {}.{}[{}]",
                              parameter.getContainingClass().getName(),
                              parameter.getMethod().getName(), parameterName);
                    object = null;
                }
                mavContainer.addAttribute(parameterName, object);
            }
        }
        return object;
    }
}

The resolver checks for the @EnumParam annotation, extracts the request value, iterates over enum constants, invokes the specified method to obtain a comparable value, and returns the matching enum constant.

JSON String Parameter Binding

When a request contains a JSON string (e.g., user={"age":12,"id":"1","name":"Foo"}), developers often parse it manually, which is repetitive. A custom @JsonParam annotation and argument resolver streamline this process.

Define Annotation

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonParam {
    String value() default "";
    Class<?> objectType() default JsonParam.class;
}
value()

works like in @EnumParam, while objectType() indicates the target class for array deserialization.

Define JSON Argument Resolver

public class JsonParamArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(JsonParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        JsonParam annotation = parameter.getParameterAnnotation(JsonParam.class);
        Object object = null;
        if (annotation != null) {
            String parameterName = annotation.value();
            if (StringUtils.isBlank(parameterName)) {
                parameterName = annotation.name();
            }
            if (StringUtils.isBlank(parameterName)) {
                parameterName = parameter.getParameterName();
            }
            String value = webRequest.getParameter(parameterName);
            if (StringUtils.isNotBlank(value)) {
                Class<?> objectType = annotation.objectType();
                try {
                    if (objectType != JsonParam.class) {
                        object = JSON.parseArray(value, objectType);
                    } else {
                        object = JSON.parseObject(value, parameter.getParameterType());
                    }
                } catch (Exception e) {
                    LoggerFactory.getLogger(JsonParamArgumentResolver.class)
                                 .error("Parameter JSON conversion failed: {}.{}[{}]",
                                        parameter.getContainingClass().getName(),
                                        parameter.getMethod().getName(), parameterName);
                    object = null;
                }
                mavContainer.addAttribute(parameterName, object);
            }
        }
        return object;
    }
}

The resolver parses the JSON string into the desired object or list, handling errors gracefully.

Spring MVC Built‑in Parameter Binding

For ordinary parameters, Spring can bind them automatically. When the request parameter name differs from the method argument name, use @RequestParam. Path variables are bound with @PathVariable. Request bodies can be bound with @RequestBody (only for JSON or other supported media types). Form data can be bound with @ModelAttribute, which does not support JSON payloads.

Appendix – Tomcat URL Character Issue

Spring Boot 2.x uses Tomcat 8+, which follows RFC 7230/3986 and rejects URLs containing characters such as {} or []. To allow these characters, configure relaxed-path-chars and relaxed-query-chars in application.yml:

server:
  tomcat:
    relaxed-path-chars: ['|','{','}','[',']']
    relaxed-query-chars: ['|','{','}','[',']']
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentCustom AnnotationSpring MVCEnum BindingJSON Parameter
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.