在 Jackson 中,你可以通过多种方式来实现自定义的序列化和反序列化控制,以下是一些常用的方式。
你可以编写自定义的序列化器(Serializer)和反序列化器(Deserializer),并将它们应用到特定的类或属性上。通过实现 JsonSerializer 和 JsonDeserializer 接口,你可以完全控制序列化和反序列化过程中的行为,包括如何读取属性、生成 JSON 或者解析 JSON 等。
当你需要对特定的类或属性进行自定义的序列化和反序列化控制时,可以通过编写自定义的序列化器(Serializer)和反序列化器(Deserializer)来实现。这样你可以完全控制序列化和反序列化过程中的行为。
以下是一个示例,展示如何使用自定义序列化器和反序列化器来控制日期格式的序列化和反序列化:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CustomDateSerializationExample {
public static class DateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
String formattedDate = DATE_FORMAT.format(value);
gen.writeString(formattedDate);
}
}
public static class DateDeserializer extends JsonDeserializer<Date> {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String dateString = p.getText();
try {
return DATE_FORMAT.parse(dateString);
} catch (Exception e) {
throw new IOException("Failed to parse date: " + dateString, e);
}
}
}
public static class Person {
private String name;
private Date birthDate;
// 省略构造函数和getter/setter方法
}
public static void main(String[] args) throws Exception {
SimpleModule module = new SimpleModule();
module.addSerializer(Date.class, new DateSerializer());
module.addDeserializer(Date.class, new DateDeserializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
// 序列化示例
Person person = new Person("John Doe", new SimpleDateFormat("yyyy-MM-dd").parse("2000-01-01"));
String json = objectMapper.writeValueAsString(person);
System.out.println(json); // 输出结果:{"name":"John Doe","birthDate":"2000-01-01"}
// 反序列化示例
String jsonInput = "{\"name\":\"Jane Smith\",\"birthDate\":\"1990-12-31\"}";
Person deserializedPerson = objectMapper.readValue(jsonInput, Person.class);
System.out.println(deserializedPerson.getBirthDate()); // 输出结果:Sat Dec 31 00:00:00 GMT 1990
}
}
通过编写自定义的序列化器和反序列化器,你可以实现更加灵活和精确的控制,以满足特定的序列化和反序列化需求。你可以根据具体的情况,编写适合的自定义序列化器和反序列化器来处理不同的类或属性。
Mix-in Annotations 允许你在不修改原始类的情况下,为其添加自定义的序列化和反序列化逻辑。你可以创建一个独立的 Mix-in 类,并在该类中为原始类添加自定义的注解,然后将 Mix-in 类与原始类关联起来。
以下是一个示例,展示如何使用 Mix-in Annotations 来控制日期格式的序列化和反序列化:
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CustomDateSerializationExample {
public static class Person {
private String name;
@JsonSerialize(using = DateSerializer.class)
@JsonDeserialize(using = DateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthDate;
// 省略构造函数和getter/setter方法
}
public static class DateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void serialize(Date value, JsonGenerator gen, com.fasterxml.jackson.databind.SerializerProvider serializers) throws IOException {
String formattedDate = DATE_FORMAT.format(value);
gen.writeString(formattedDate);
}
}
public static class DateDeserializer extends JsonDeserializer<Date> {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String dateString = p.getText();
try {
return DATE_FORMAT.parse(dateString);
} catch (Exception e) {
throw new IOException("Failed to parse date: " + dateString, e);
}
}
}
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
// 注册 Mix-in Annotations
objectMapper.addMixIn(Person.class, PersonMixin.class);
// 序列化示例
Person person = new Person("John Doe", new SimpleDateFormat("yyyy-MM-dd").parse("2000-01-01"));
String json = objectMapper.writeValueAsString(person);
System.out.println(json); // 输出结果:{"name":"John Doe","birthDate":"2000-01-01"}
// 反序列化示例
String jsonInput = "{\"name\":\"Jane Smith\",\"birthDate\":\"1990-12-31\"}";
Person deserializedPerson = objectMapper.readValue(jsonInput, Person.class);
System.out.println(deserializedPerson.getBirthDate()); // 输出结果:Sat Dec 31 00:00:00 GMT 1990
}
// Mix-in Annotations 类
abstract class PersonMixin {
@JsonSerialize(using = DateSerializer.class)
@JsonDeserialize(using = DateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd")
abstract Date getBirthDate();
}
}
通过使用 Mix-in Annotations,你可以在不修改原始类的情况下,为其添加自定义的序列化和反序列化逻辑。这种方法非常灵活,适用于需要对多个类或属性进行自定义序列化和反序列化控制的场景。
Jackson 提供了多个注解,如 @JsonSerialize 和 @JsonDeserialize,它们可以直接应用于类或属性上,用来指定自定义的序列化器和反序列化器。这样你可以针对特定的类或属性,指定自定义的序列化和反序列化逻辑。
使用 @JsonSerialize 和 @JsonDeserialize 注解时,你可以为特定属性指定自定义的序列化器和反序列化器。下面是一个更简化的示例,演示如何在类中直接使用这两个注解来实现自定义的序列化和反序列化控制:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonFormat;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CustomDateSerializationExample {
public static class Person {
private String name;
@JsonSerialize(using = DateSerializer.class)
@JsonDeserialize(using = DateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthDate;
// 省略构造函数和getter/setter方法
}
public static class DateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void serialize(Date value, JsonGenerator gen, com.fasterxml.jackson.databind.SerializerProvider serializers) throws IOException {
String formattedDate = DATE_FORMAT.format(value);
gen.writeString(formattedDate);
}
}
public static class DateDeserializer extends JsonDeserializer<Date> {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String dateString = p.getText();
try {
return DATE_FORMAT.parse(dateString);
} catch (Exception e) {
throw new IOException("Failed to parse date: " + dateString, e);
}
}
}
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
// 序列化示例
Person person = new Person("John Doe", new SimpleDateFormat("yyyy-MM-dd").parse("2000-01-01"));
String json = objectMapper.writeValueAsString(person);
System.out.println(json); // 输出结果:{"name":"John Doe","birthDate":"2000-01-01"}
// 反序列化示例
String jsonInput = "{\"name\":\"Jane Smith\",\"birthDate\":\"1990-12-31\"}";
Person deserializedPerson = objectMapper.readValue(jsonInput, Person.class);
System.out.println(deserializedPerson.getBirthDate()); // 输出结果:Sat Dec 31 00:00:00 GMT 1990
}
}
通过这种方式,你可以直接在属性上指定自定义的序列化器和反序列化器,从而实现对该属性的序列化和反序列化控制。这种方式非常简洁,适用于只需要对少量属性进行自定义序列化和反序列化控制的场景。
通过配置 ObjectMapper,你可以注册自定义的模块或者处理器,比如 SimpleModule 或者 HandlerInstantiator,来实现更高级的自定义序列化和反序列化控制。这些方法可以让你在全局范围内对序列化和反序列化行为进行定制。
下面是一个简单的示例,演示如何使用 SimpleModule 和自定义的 HandlerInstantiator 来注册自定义的序列化器和反序列化器:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.module.SimpleAbstractTypeResolver;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import com.fasterxml.jackson.databind.module.SimpleSerializers;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CustomDateSerializationExample {
public static class Person {
private String name;
private Date birthDate;
// 省略构造函数和getter/setter方法
}
public static class DateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void serialize(Date value, JsonGenerator gen, com.fasterxml.jackson.databind.SerializerProvider serializers) throws IOException {
String formattedDate = DATE_FORMAT.format(value);
gen.writeString(formattedDate);
}
}
public static class DateDeserializer extends JsonDeserializer<Date> {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String dateString = p.getText();
try {
return DATE_FORMAT.parse(dateString);
} catch (ParseException e) {
throw new IOException("Failed to parse date: " + dateString, e);
}
}
}
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
// 创建自定义模块
SimpleModule customModule = new SimpleModule();
// 注册自定义的序列化器和反序列化器
customModule.addSerializer(Date.class, new DateSerializer());
customModule.addDeserializer(Date.class, new DateDeserializer());
// 注册自定义模块到ObjectMapper
objectMapper.registerModule(customModule);
// 序列化示例
Person person = new Person("John Doe", new SimpleDateFormat("yyyy-MM-dd").parse("2000-01-01"));
String json = objectMapper.writeValueAsString(person);
System.out.println(json); // 输出结果:{"name":"John Doe","birthDate":"2000-01-01"}
// 反序列化示例
String jsonInput = "{\"name\":\"Jane Smith\",\"birthDate\":\"1990-12-31\"}";
Person deserializedPerson = objectMapper.readValue(jsonInput, Person.class);
System.out.println(deserializedPerson.getBirthDate()); // 输出结果:Sat Dec 31 00:00:00 GMT 1990
}
}
除了使用 SimpleModule,你还可以通过实现自定义的 HandlerInstantiator 类来提供更复杂的定制化逻辑,以满足更高级的序列化和反序列化需求。HandlerInstantiator 可以用于创建自定义的序列化器、反序列化器、值处理器等。
这个接口允许你在序列化过程中动态地修改要应用的序列化器。通过实现这些接口,你可以根据特定的条件或者属性来动态地改变序列化器的行为。
这些方法提供了灵活的方式来实现自定义的序列化和反序列化控制,你可以根据具体的需求选择最适合的方式来实现自定义行为。
下面是一个简单的示例,演示如何使用 BeanSerializerModifier 来实现自定义的序列化控制:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.util.List;
public class CustomSerializationExample {
public static class Person {
private String name;
private String email;
// 省略构造函数和getter/setter方法
}
public static class UpperCaseSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter writer : beanProperties) {
if (writer.getName().equals("email")) {
writer.assignSerializer(new UpperCaseStringSerializer());
}
}
return beanProperties;
}
}
public static class UpperCaseStringSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.toUpperCase()); // 自定义字符串序列化为大写形式
}
}
public static void main(String[] args) {
SimpleModule module = new SimpleModule("CustomModule");
module.setSerializerModifier(new UpperCaseSerializerModifier());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
Person person = new Person("John Doe", "john@example.com");
try {
String json = objectMapper.writeValueAsString(person);
System.out.println(json); // 输出结果:{"name":"John Doe","email":"JOHN@EXAMPLE.COM"}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
实际使用场景:java返回对象中关于枚举、字典数据的自动转化
以下是具体的代码实现,有些地方需要用户根据实际情况自己实现,比如从字典获取数据等。
import java.lang.annotation.*;
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Dict {
static enum Void {}
Class<? extends Enum<?>> enumType() default Void.class;
/**
* 默认值,获取不到字典则使用默认值
*/
String defaultValue() default "";
}
自定义BeanSerializerModifier,以下代码中DictConstants.getDictCacheKey(valueStr)只是自定义key,需要自己实现。
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.hg.dcm.commons.core.HGBusinessException;
import com.hg.dcm.commons.core.SpringContextUtil;
import com.hg.energy.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@Slf4j
public class DictSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter beanProperty : beanProperties) {
Dict dict = beanProperty.getAnnotation(Dict.class);
if (dict != null) {
beanProperty.assignSerializer(new DictSerializer(dict));
}
}
return beanProperties;
}
/**
* 字典自定义序列化
*/
static class DictSerializer extends JsonSerializer<Object> {
/**
* 生成序列化字段后缀
*/
private static final String LABEL_SUFFIX = "Desc";
/**
* 字典配置信息
*/
private final Dict dict;
/**
* 枚举获取key方法
*/
private static final String[] KEY_METHODS = {"getValue", "getCode", "getStatus", "name"};
/**
* 获取枚举描述方法
*/
private static final String[] DESC_METHODS = {"getDesc"};
/**
* 构造方法
*
* @param dict 字典描述
*/
public DictSerializer(Dict dict) {
this.dict = dict;
}
/**
* Method that can be called to ask implementation to serialize
* values of type this serializer handles.
*
* @param value Value to serialize; can <b>not</b> be null.
* @param gen Generator used to output resulting Json content
* @param provider Provider that can be used to get serializers for
* serializing Objects value contains, if any.
*/
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
provider.defaultSerializeValue(value, gen);
// 添加转换之后的字段:xxxDesc
String fieldName = gen.getOutputContext().getCurrentName();
gen.writeStringField(fieldName.concat(LABEL_SUFFIX), value != null ? this.getDesc(dict, value) : null);
}
/**
* 获取字典信息
* TODO
*
* @param dict 字典对象
* @param value 字典值
* @return
*/
private String getDesc(Dict dict, Object value) {
try {
// 先查询是否是枚举类型,查到则返回
String enumDesc = this.getEnumDesc(dict, value);
if (enumDesc != null) {
return enumDesc;
}
String valueStr = Objects.toString(value);
//获取缓存key,可以自定义
String key = DictConstants.getDictCacheKey(valueStr);
// Redis 缓存操作类 这里建议优先使用本地缓存, 本地缓存 -> redis -> Db
RedisService redis = SpringContextUtil.getBean(RedisService.class);
if (redis.exists(key)) {
return redis.get(key);
}
// 数据库字典操作类
//redis.setEx(key, desc, 1, TimeUnit.HOURS);
return "字典数据";
} catch (Exception e) {
log.error("字典转换:获取字典描述异常,使用默认值:{},key:{}, dict:{}, 异常:{}", dict.defaultValue(), value, dict.enumType(), e.getMessage(), e);
return dict.defaultValue();
}
}
/**
* 获取枚举类型的描述信息
*
* @param dict 字典
* @param value 值
* @return 枚举desc字段
*/
private String getEnumDesc(Dict dict, Object value) throws InvocationTargetException, IllegalAccessException {
if (dict == null || value == null) {
return null;
}
Class<? extends Enum<?>> et = dict.enumType();
if (Dict.Void.class.equals(et)) {
return null;
}
Enum<?>[] enums = et.getEnumConstants();
Method keyMethod = this.getMethod(et, KEY_METHODS);
if (keyMethod == null) {
// 自定义业务异常
throw new HGBusinessException(String.format("字典转换:枚举:%s,没有方法:%s", et.getName(), Arrays.toString(KEY_METHODS)));
}
Method descMethod = this.getMethod(et, DESC_METHODS);
if (descMethod == null) {
throw new HGBusinessException(String.format("字典转换:枚举:%s,没有方法:%s", et.getName(), Arrays.toString(DESC_METHODS)));
}
for (Enum<?> e : enums) {
if (value.equals(keyMethod.invoke(e))) {
return Objects.toString(descMethod.invoke(e));
}
}
log.error("字典转换:通过枚举转换失败,枚举:{},值:{},KeyMethod:{},DescMethod:{}", et.getName(), value, Arrays.toString(KEY_METHODS), Arrays.toString(DESC_METHODS));
throw new HGBusinessException(String.format("字典转换失败,枚举:%s,值:%s", et.getName(), value));
}
/**
* 获取读方法
*
* @param enumType 枚举类
* @param methodNames 方法名称
* @return Method
*/
private Method getMethod(Class<? extends Enum<?>> enumType, String... methodNames) {
for (String methodName : methodNames) {
try {
return enumType.getMethod(methodName);
} catch (NoSuchMethodException e) {
}
}
return null;
}
}
}
自定义MappingJackson2HttpMessageConverter
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
SimpleModule simpleModule = new SimpleModule().setSerializerModifier(new DictSerializerModifier());
builder.modules(simpleModule);
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return new MappingJackson2HttpMessageConverter(builder.build());
}
将自定义的MappingJackson2HttpMessageConverter加入到HttpMessageConverters中
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0,mappingJackson2HttpMessageConverter());
}
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
SimpleModule simpleModule = new SimpleModule().setSerializerModifier(new DictSerializerModifier());
builder.modules(simpleModule);
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return new MappingJackson2HttpMessageConverter(builder.build());
}
}
术因分享而日新,每获新知,喜溢心扉。 诚邀关注公众号 『
码到三十五
』 ,获取更多技术资料。