-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Determine enum values by serializing in Jackson module #137
Comments
Hi Luke, Thank you for your input. You’re actually not the first to suggest making use of the already configured custom serializers. What Jackson does, is producing (part of) a JSON instance – not a schema. How do you propose to produce all possible outputs and derive a common schema from that?
If you could provide your custom serializer and/or examples of an enum and its expected representation as a schema, we could come up with something. Currently, I’m assuming it’ll need to be a Cheers, |
Hi Carsten, Here's the module I am using to control how enums are serialized. It is using Guava's CaseFormat. public final class EnumCaseConversionJacksonModule extends SimpleModule {
private final CaseFormat deserialized;
private final CaseFormat serialized;
public EnumCaseConversionJacksonModule(CaseFormat serialized) {
this(CaseFormat.UPPER_UNDERSCORE, serialized);
}
public EnumCaseConversionJacksonModule(CaseFormat deserialized, CaseFormat serialized) {
super("enum case conversion");
this.deserialized = deserialized;
this.serialized = serialized;
}
{
setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<Enum> modifyEnumDeserializer(
DeserializationConfig config,
JavaType type,
BeanDescription beanDesc,
JsonDeserializer deserializer
) {
// Only use custom deserializer if @JsonCreator isn't specified
@SuppressWarnings("ConstantConditions")
boolean hasCreator = Iterables.any(beanDesc.getFactoryMethods(), m -> m.hasAnnotation(JsonCreator.class));
if (hasCreator) {
@SuppressWarnings("unchecked")
JsonDeserializer<Enum> enumJsonDeserializer = (JsonDeserializer<Enum>) super.modifyEnumDeserializer(config, type, beanDesc, deserializer);
return enumJsonDeserializer;
} else {
return new JsonDeserializer<Enum>() {
@Override
public Enum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getValueAsString();
if (StringUtils.isBlank(value)) {
return null;
} else {
return toEnum(type.getRawClass(), value);
}
}
private <T extends Enum<T>> Enum<T> toEnum(Class<?> rawClass, String value) {
Class<T> cast = Types.cast(rawClass);
return Enum.valueOf(cast, serialized.to(deserialized, value));
}
};
}
}
});
setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifyEnumSerializer(SerializationConfig config, JavaType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
// Only use custom serializer if @JsonValue isn't specified
if (beanDesc.findJsonValueAccessor() == null) {
return new StdSerializer<Enum>(Enum.class) {
@Override
public void serialize(Enum value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeString(deserialized.to(serialized, value.name()));
}
};
}
return super.modifyEnumSerializer(config, valueType, beanDesc, serializer);
}
});
}
}
I think enums are a bit of a special case here and am only suggesting to use this strategy for that. I don't see it being feasible for arbitrary types. My use case is that I want conventional Java enums, but lower-camel symbols in the data file. And, I want to achieve this without having to markup all of my enum classes. I'm very interested in any pointers on how to achieve the same with a custom definition provider. |
FYI FasterXML/jackson-databind#2667 is relevant here. |
Hi Luke, Once that feature is introduced in Jackson, I'm happy to add general support for it. ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new EnumCaseConversionJacksonModule(CaseFormat.LOWER_CAMEL));
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON);
configBuilder.with(new EnumModule(possibleEnumValue -> {
try {
String valueInQuotes = objectMapper.writeValueAsString(possibleEnumValue);
return valueInQuotes.substring(1, valueInQuotes.length() - 1);
} catch (JsonProcessingException ex) {
return null;
}
}));
SchemaGenerator generator = new SchemaGenerator(configBuilder.build());
JsonNode jsonSchema = generator.generateSchema(TestEnum.class); For this example: enum TestEnum {
FIRST_VALUE, SECOND_VALUE;
} The produced schema looks like this: {
"$schema" : "https://json-schema.org/draft/2019-09/schema",
"type" : "string",
"enum" : [ "firstValue", "secondValue" ]
} |
I guess I could add that as a standard The need for an |
If using a custom global enum serializer registered with an object mapper, the inferred values in the schema are incorrect. Instead of reverse engineering how Jackson will serialize the value, it might be better to just serialize through Jackson after allowing the user to register whatever modules/serializers they need with the mapper used for this serialization.
The text was updated successfully, but these errors were encountered: