Jackson第三方库嵌入对象自定义序列化及反序列化方案问询
可行方案推荐
方案1:Mixin + 通用LocalizedTexts序列化/反序列化器
既然LocalizedTexts是第三方类无法修改,用Jackson Mixin给它绑定自定义序列化规则,同时给Article类通过Mixin忽略原有多语言属性,额外生成统一的translations数组。这种方式不用修改领域类源码,也能避免全反射的冗余。
示例代码:
// 给第三方LocalizedTexts类绑定Mixin,指定序列化器 abstract class LocalizedTextsMixin { @JsonSerialize(using = LocalizedTextsSerializer.class) abstract Map<Language, String> getTexts(); // 假设第三方类有此方法获取多语言映射 } // 通用序列化器:把单个LocalizedTexts转成{lang: "...", value: "..."}结构 public class LocalizedTextsSerializer extends StdSerializer<LocalizedTexts> { public LocalizedTextsSerializer() { super(LocalizedTexts.class); } @Override public void serialize(LocalizedTexts value, JsonGenerator gen, SerializerProvider provider) throws IOException { for (Map.Entry<Language, String> entry : value.getTexts().entrySet()) { gen.writeStartObject(); gen.writeStringField("lang", entry.getKey().name()); gen.writeStringField("value", entry.getValue()); gen.writeEndObject(); } } } // 给Article类绑定Mixin,处理translations数组生成 abstract class ArticleMixin { // 忽略原有LocalizedTexts类型的属性 @JsonIgnore abstract LocalizedTexts getTitle(); @JsonIgnore abstract LocalizedTexts getContent(); // 自定义生成统一的translations数组 @JsonProperty("translations") List<Object> getTranslations() { // 属性少的话直接硬编码,比反射更高效 List<Object> translations = new ArrayList<>(); translations.addAll(Arrays.asList(getTitle(), getContent())); return translations; } } // 注册到ObjectMapper ObjectMapper mapper = new ObjectMapper(); mapper.addMixIn(LocalizedTexts.class, LocalizedTextsMixin.class); mapper.addMixIn(Article.class, ArticleMixin.class);
反序列化时,写对应的LocalizedTextsDeserializer和ArticleDeserializer,同样通过Mixin绑定到类上,把translations数组拆分回原属性即可。
方案2:全局修改序列化行为(BeanSerializerModifier)
如果多个领域类都有LocalizedTexts属性,用BeanSerializerModifier全局处理,自动收集所有多语言属性到translations数组,不用给每个类单独配置Mixin。反射只在初始化时执行一次,不会产生冗余。
示例代码:
public class LocalizedTextModifier extends BeanSerializerModifier { @Override public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) { List<BeanPropertyWriter> filteredProps = new ArrayList<>(); List<BeanPropertyWriter> localizedProps = new ArrayList<>(); // 分离LocalizedTexts类型属性和普通属性 for (BeanPropertyWriter writer : beanProperties) { if (LocalizedTexts.class.isAssignableFrom(writer.getType().getRawClass())) { localizedProps.add(writer); } else { filteredProps.add(writer); } } // 添加自定义的translations属性 filteredProps.add(new BeanPropertyWriter() { @Override public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception { gen.writeFieldName("translations"); gen.writeStartArray(); for (BeanPropertyWriter prop : localizedProps) { LocalizedTexts texts = (LocalizedTexts) prop.get(bean); for (Map.Entry<Language, String> entry : texts.getTexts().entrySet()) { gen.writeStartObject(); gen.writeStringField("lang", entry.getKey().name()); gen.writeStringField("value", entry.getValue()); gen.writeEndObject(); } } gen.writeEndArray(); } }); return filteredProps; } } // 注册到ObjectMapper ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new SimpleModule() { @Override public void setupModule(SetupContext context) { super.setupModule(context); context.addBeanSerializerModifier(new LocalizedTextModifier()); } });
反序列化用对应的BeanDeserializerModifier,全局解析translations数组并拆分回各个LocalizedTexts属性即可。
方案3:DTO转换(最直观,无反射)
如果Article结构不频繁变动,直接写存储用的DTO,手动完成领域对象和DTO的双向转换,完全避免Jackson复杂配置和反射开销,维护成本最低。
示例代码:
// 存储用的DTO public class ArticleDTO { private String id; private List<TranslationEntry> translations; // 其他普通属性... public static class TranslationEntry { private String lang; private String value; private String field; // 标记归属的领域属性,比如"title"或"content" // getter/setter } // 领域对象转DTO public static ArticleDTO fromDomain(Article article) { ArticleDTO dto = new ArticleDTO(); dto.setId(article.getId()); List<TranslationEntry> entries = new ArrayList<>(); // 处理title属性的多语言条目 for (Map.Entry<Language, String> entry : article.getTitle().getTexts().entrySet()) { TranslationEntry te = new TranslationEntry(); te.setLang(entry.getKey().name()); te.setValue(entry.getValue()); te.setField("title"); entries.add(te); } // 处理content属性的多语言条目 for (Map.Entry<Language, String> entry : article.getContent().getTexts().entrySet()) { TranslationEntry te = new TranslationEntry(); te.setLang(entry.getKey().name()); te.setValue(entry.getValue()); te.setField("content"); entries.add(te); } dto.setTranslations(entries); return dto; } // DTO转领域对象 public Article toDomain() { Article article = new Article(); article.setId(this.id); LocalizedTexts title = new LocalizedTexts(); LocalizedTexts content = new LocalizedTexts(); for (TranslationEntry entry : this.translations) { Language lang = Language.valueOf(entry.getLang()); switch (entry.getField()) { case "title": title.addText(lang, entry.getValue()); // 假设第三方类有此添加方法 break; case "content": content.addText(lang, entry.getValue()); break; } } article.setTitle(title); article.setContent(content); return article; } }
序列化时先把Article转成DTO再写入数据库,读取时先反序列化为DTO再转回领域对象即可。
内容的提问来源于stack exchange,提问作者thanili
相关产品推荐
相关产品推荐

