自定义Converter对RequestParam生效,对RequestBody字段无效问题
CountryEnumConverter Isn’t Working for @RequestBody The key issue here is that Spring uses different deserialization mechanisms for request parameters vs. JSON request bodies:
- For
@RequestParam, Spring relies onConverter<String, T>implementations to convert incoming string parameters to your enum type. That’s why your converter works here. - For
@RequestBody, Spring uses Jackson’sObjectMapper(viaMappingJackson2HttpMessageConverter) to parse JSON into objects. YourConverterisn’t part of this JSON deserialization pipeline, so it never gets called.
Here are the most straightforward solutions to fix this:
Solution 1: Add a @JsonCreator Method to Your Enum
The simplest approach is to tell Jackson to use your existing getBySign method to deserialize the enum from a string. Just add the @JsonCreator annotation to the method:
public enum CountryEnum { US("us"), CA("ca"); private final String sign; CountryEnum(String sign) { this.sign = sign; } @JsonCreator // This tells Jackson to use this method for deserialization public static CountryEnum getBySign(String country) { CountryEnum countryEnum = Arrays.stream(values()) .filter(c -> c.sign.equalsIgnoreCase(country)) .findFirst() .orElseThrow(() -> new IllegalArgumentException(country + " - Country is not supported!")); return countryEnum; } // Optional getter for the sign field public String getSign() { return sign; } }
This way, when Jackson encounters a string in the JSON body mapped to a CountryEnum field, it will call getBySign to convert the string to the correct enum value.
Solution 2: Create a Custom Jackson Deserializer
If you prefer to keep deserialization logic separate from the enum, you can create a dedicated Jackson deserializer and register it:
Step 1: Implement the Deserializer
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; public class CountryEnumDeserializer extends StdDeserializer<CountryEnum> { public CountryEnumDeserializer() { super(CountryEnum.class); } @Override public CountryEnum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String countrySign = p.getValueAsString(); CountryEnum countryEnum = CountryEnum.getBySign(countrySign); if (countryEnum == null) { throw new IllegalArgumentException(countrySign + " - Country is not supported!"); } return countryEnum; } }
Step 2: Register the Deserializer
You can register it either per field in your MyBody class:
public class MyBody { @NotNull @JsonDeserialize(using = CountryEnumDeserializer.class) // Apply to this field private CountryEnum country; // Constructors, getters, setters }
Or globally for all CountryEnum fields in your Spring configuration:
import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JacksonConfig { @Bean public SimpleModule countryEnumDeserializerModule() { SimpleModule module = new SimpleModule(); module.addDeserializer(CountryEnum.class, new CountryEnumDeserializer()); return module; } }
Solution 3: Reuse Your Existing Converter with Jackson
If you want to avoid duplicating logic and reuse your CountryEnumConverter, you can configure Jackson to use Spring’s ConversionService:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.ConversionService; import java.io.IOException; @Configuration public class JacksonConfig { private final ConversionService conversionService; public JacksonConfig(ConversionService conversionService) { this.conversionService = conversionService; } @Bean public SimpleModule conversionServiceModule() { SimpleModule module = new SimpleModule(); module.addDeserializer(Enum.class, new StdDeserializer<Enum>(Enum.class) { @Override public Enum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String value = p.getValueAsString(); Class<?> enumType = ctxt.getContextualType().getRawClass(); return (Enum) conversionService.convert(value, enumType); } }); return module; } }
This will make Jackson use Spring’s converters for all enum types, so your existing CountryEnumConverter will be used for @RequestBody fields too.
内容的提问来源于stack exchange,提问作者stefan.stt

