Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Json deserialization of enum, forbid int
I have a DTO that contains an enum
field:
@Getter
@Setter
static class Foo {
Bar bar;
}
enum Bar {
X, Y
}
When I deserialize a JSON, it allows int
as values:
var objectMapper = new ObjectMapper();
Foo foo = objectMapper.readValue("""
{"bar": 1}"
""",
Foo.class
);
I expect an error here, but get the value Bar.Y
.
How can I validate a JSON so only string values for enum
are allowed?
Here's the JSON library dependency:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
2 answers
The following users marked this post as Works for me:
User | Comment | Date |
---|---|---|
talex | (no comment) | Sep 24, 2024 at 19:11 |
This is something that could go really wrong. I remember using this, and it was fine initially, until the enum
in question evolved, and the ordinals changed, so the values didn't correspond anymore to what the initial assumption was.
Anyway, long story short, if you would like to prevent this system-wide (and I recommend it), configure ObjectMapper
to prevent using the Enum's ordinals with DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS
.
Something like this should work:
final var mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
If you are using Spring Boot
you can achieve this modifying application.yaml
:
spring:
jackson:
deserialization:
fail-on-numbers-for-enums: true
0 comment threads
One solution is to create a method in the enum
that takes a String
and converts it to the correspondent value. Then we annotate this method with @JsonCreator
:
import com.fasterxml.jackson.annotation.JsonCreator;
enum Bar {
X, Y;
@JsonCreator
public static Bar fromString(String value) {
return Bar.valueOf(value);
}
}
Now only valid strings are accepted. In this case, {"bar": "X"}
and {"bar": "Y"}
. Any other value (such as {"bar": "A"}
or {"bar": 1}
) will raise an exception.
Another solution is to create your own deserializer:
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonMappingException;
import java.io.IOException;
public class BarDeserializer extends JsonDeserializer<Bar> {
@Override
public Bar deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JacksonException {
JsonToken jsonToken = jp.getCurrentToken();
if (jsonToken == JsonToken.VALUE_STRING) { // value is a string
try {
return Bar.valueOf(jp.getValueAsString());
} catch (IllegalArgumentException | NullPointerException e) {
throw new JsonMappingException(jp, "Invalid value for enum Bar", e);
}
}
// value is not a string
throw new JsonMappingException(jp, "Value must be a string");
}
}
Then you annotate the enum
with it:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonDeserialize(using = BarDeserializer.class)
enum Bar {
X, Y;
}
0 comment threads