您如何修改未注释枚举的默认枚举(反)序列化,但在 Jackson 中保留标准行为(@JsonProperty/@JsonValue/...)?
How do you modify default enum (de)serialization for non-annotated enums but retain standard behavior (@JsonProperty/@JsonValue/...) in Jackson?
目前 jackson(未定制)像这样序列化枚举:
- 如果有
@JsonProperty
或@JsonValue
,则用于派生序列化名称
- 否则,执行标准序列化:可能使用索引或
toString()
(取决于映射器设置),或者默认调用.name()
反序列化是这样的:
- 如果有
@JsonProperty
、@JsonValue
或@JsonCreator
,它们会影响反序列化
- 否则,将使用镜像序列化的标准反序列化。
我想在这两种情况下都保留第 1 项(即仍然支持所有 3 个注释,如果我遗漏了什么,则支持更多),但修改 'default case' 的行为:例如,始终将枚举序列化为.name().toLowercase()
如果没有注释。
我可以使用 addSerializer()
、addDeserializer()
或(反)序列化器修改机制,但其中 none 允许简单而优雅的解决方案。我所能发明的只是 copy/paste 来自杰克逊的大量代码(丑陋且脆弱),或者使用修改机制,反省枚举 class 模拟杰克逊的相关逻辑以识别 [=43] 时的情况=] 逻辑将适用,然后使用我的 'to-lower-case' 策略。
有没有更好的方法修改'default'序列化策略,同时仍然保留所有'non-default'个案例?
注解@JsonCreator
用于反序列化(是静态工厂)
注解@JsonValue
用于序列化
给定的例子对我有用:
class MyException extends RuntimeException {}
enum MyEnum {
TEST;
private final String value;
private MyEnum() {
this.value = this.name().toLowerCase();
}
@JsonValue
public final String value() {
return this.value;
}
@JsonCreator
public static MyEnum forValue(String value) {
return Arrays.stream(MyEnum.values()).filter(myEnum -> myEnum.value.equals(value)).findFirst().orElseThrow(MyException::new);
}
}
您可以使用此技术插入新的 AnnotationIntrospector whose findEnumValues
method changes the enum name. The EnumRenamingModule
from the therapi-json-rpc 项目。 (免责声明:这是我的宠物项目。)这是从 GitHub 存储库复制的代码:
package com.github.therapi.jackson.enums;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
* Customizes the way Jackson serializes enums.
*/
public abstract class EnumRenamingModule extends SimpleModule {
private boolean overrideExistingNames;
public EnumRenamingModule() {
super("therapi-enum-renaming");
}
/**
* Configures the module to clobber any enum names set by
* a previous annotation introspector.
*/
public EnumRenamingModule overrideExistingNames() {
this.overrideExistingNames = true;
return this;
}
@Override
public void setupModule(Module.SetupContext context) {
super.setupModule(context);
context.insertAnnotationIntrospector(new EnumNamingAnnotationIntrospector());
}
private class EnumNamingAnnotationIntrospector extends NopAnnotationIntrospector {
public String[] findEnumValues(Class<?> enumType, Enum<?>[] enumValues, String[] names) {
for (int i = 0; i < enumValues.length; i++) {
if (names[i] == null || overrideExistingNames) {
names[i] = EnumRenamingModule.this.getName(enumValues[i]);
}
}
return names;
}
}
/**
* @param value the enum value to inspect
* @return the JSON name for the enum value, or {@code null} to delegate to the next introspector
*/
protected abstract String getName(Enum<?> value);
}
创建一个子类来做你想要的转换:
public class LowerCaseEnumModule extends EnumRenamingModule {
@Override protected String getName(Enum<?> value) {
return value.name().toLowerCase(Locale.ROOT);
}
}
并使用您的 ObjectMapper 注册它:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new LowerCaseEnumModule());
目前 jackson(未定制)像这样序列化枚举:
- 如果有
@JsonProperty
或@JsonValue
,则用于派生序列化名称 - 否则,执行标准序列化:可能使用索引或
toString()
(取决于映射器设置),或者默认调用.name()
反序列化是这样的:
- 如果有
@JsonProperty
、@JsonValue
或@JsonCreator
,它们会影响反序列化 - 否则,将使用镜像序列化的标准反序列化。
我想在这两种情况下都保留第 1 项(即仍然支持所有 3 个注释,如果我遗漏了什么,则支持更多),但修改 'default case' 的行为:例如,始终将枚举序列化为.name().toLowercase()
如果没有注释。
我可以使用 addSerializer()
、addDeserializer()
或(反)序列化器修改机制,但其中 none 允许简单而优雅的解决方案。我所能发明的只是 copy/paste 来自杰克逊的大量代码(丑陋且脆弱),或者使用修改机制,反省枚举 class 模拟杰克逊的相关逻辑以识别 [=43] 时的情况=] 逻辑将适用,然后使用我的 'to-lower-case' 策略。
有没有更好的方法修改'default'序列化策略,同时仍然保留所有'non-default'个案例?
注解@JsonCreator
用于反序列化(是静态工厂)
注解@JsonValue
用于序列化
给定的例子对我有用:
class MyException extends RuntimeException {}
enum MyEnum {
TEST;
private final String value;
private MyEnum() {
this.value = this.name().toLowerCase();
}
@JsonValue
public final String value() {
return this.value;
}
@JsonCreator
public static MyEnum forValue(String value) {
return Arrays.stream(MyEnum.values()).filter(myEnum -> myEnum.value.equals(value)).findFirst().orElseThrow(MyException::new);
}
}
您可以使用此技术插入新的 AnnotationIntrospector whose findEnumValues
method changes the enum name. The EnumRenamingModule
from the therapi-json-rpc 项目。 (免责声明:这是我的宠物项目。)这是从 GitHub 存储库复制的代码:
package com.github.therapi.jackson.enums;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
* Customizes the way Jackson serializes enums.
*/
public abstract class EnumRenamingModule extends SimpleModule {
private boolean overrideExistingNames;
public EnumRenamingModule() {
super("therapi-enum-renaming");
}
/**
* Configures the module to clobber any enum names set by
* a previous annotation introspector.
*/
public EnumRenamingModule overrideExistingNames() {
this.overrideExistingNames = true;
return this;
}
@Override
public void setupModule(Module.SetupContext context) {
super.setupModule(context);
context.insertAnnotationIntrospector(new EnumNamingAnnotationIntrospector());
}
private class EnumNamingAnnotationIntrospector extends NopAnnotationIntrospector {
public String[] findEnumValues(Class<?> enumType, Enum<?>[] enumValues, String[] names) {
for (int i = 0; i < enumValues.length; i++) {
if (names[i] == null || overrideExistingNames) {
names[i] = EnumRenamingModule.this.getName(enumValues[i]);
}
}
return names;
}
}
/**
* @param value the enum value to inspect
* @return the JSON name for the enum value, or {@code null} to delegate to the next introspector
*/
protected abstract String getName(Enum<?> value);
}
创建一个子类来做你想要的转换:
public class LowerCaseEnumModule extends EnumRenamingModule {
@Override protected String getName(Enum<?> value) {
return value.name().toLowerCase(Locale.ROOT);
}
}
并使用您的 ObjectMapper 注册它:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new LowerCaseEnumModule());