使用 Jackson 根据 API 版本指定不同的 JSON 属性名称
Posted
技术标签:
【中文标题】使用 Jackson 根据 API 版本指定不同的 JSON 属性名称【英文标题】:Specifying different JSON property names according to API version with Jackson 【发布时间】:2016-12-03 13:55:54 【问题描述】:我需要能够同时支持多个 API 版本,使用 Jackson 来序列化/反序列化对象。我探索了以下解决方案:
@JsonProperty 属性命名策略 混合注释但是,每一个都会导致自己的问题。如果我可以直接在注释中添加多个具有正确名称的版本,@JsonProperty 将是一个完美的解决方案:
@JsonProperty(api="1.5", "fname")
@JsonProperty(api="1.6", "firstname")
String firstName;
随着时间的推移,这可能会变得很长,但肯定会很容易理解。但是,这似乎是不可能的。
PropertyNamingStrategy 和 mixins 也是一个好主意。事实上,我尝试了 mixin 注释(例如,Inherit model with different JSON property names)并且它们起作用了,但是这两种解决方案都存在一个问题。您必须在某处指定和使用 ObjectMapper(也可能是 ObjectReader/Writer)。
这很痛苦,因为对象的层次结构如下所示:
实体
|--用户
|--组
|--凭证
等等。实体包含通用属性,例如名称、id、描述、状态和 API 版本。假设您现在执行以下操作:
User user = new User("catherine", "stewardess", "active");
user.setApiVersion(Entity.ApiVersion.V2);
if(user.getVersion() == Entity.ApiVersion.V2)
MAPPER.addMixin(Entity.class, EntityMixinV2.class);
String userJson = MAPPER.writeValueAsString(user);
User user2 = MAPPER.readValue(userJson);
System.out.println(MAPPER.writeValueAsString(user2));
MAPPER
只是在别处定义的 ObjectMapper,EntityMixinV2
类似于:
public abstract class EntityMixinV2
@JsonProperty("employmentState")
String state;
覆盖 User 的父类 Entity 中的变量之一(在本例中为 state
)。这有几个问题:
理想情况下,我希望能够使用类似上面的 @JsonProperty 示例,因为名称更改是问题,而不是我对变量所做的事情。我也考虑过使用
@JsonSerialize(using = EntitySerializer.class)
@JsonDeserialize(using = EntityDeserializer.class)
注解,但这只会改变变量的内容,而不是属性名称本身。
【问题讨论】:
【参考方案1】:您可以创建自定义注释来定义字段的可能版本的属性名称列表,并创建自定义JacksonAnnotationIntrospector
以根据给定版本解析属性名称。
自定义注解如下所示:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface VersioningProperties
Property[] value();
@interface Property
String version();
String value();
自定义的JacksonAnnotationIntrospector
看起来像:
public class VersioningPropertiesIntrospector
private String version;
public VersioningPropertiesIntrospector(String version)
this.version = version;
@Override
pubilc PropertyName findNameForSerialization(Annotated a)
PropertyName propertyName = findNameFromVersioningProperties(a);
if (propertyName != null)
return propertyName;
return super.findNameForSerialization(a);
@Override
pubilc PropertyName findNameForDeserialization(Annotated a)
PropertyName propertyName = findNameFromVersioningProperties(a);
if (propertyName != null)
return propertyName;
return super.findNameForDeserialization(a);
private PropertyName findNameFromVersioningProperties(Annotated a)
VersioningProperties annotation = a.getAnnotation(VersioningProperties.class);
if (annotation == null)
return null;
for (Property property : annotation.value())
if (version.equals(property.version())
return new PropertyName(property.value());
return null;
注解使用示例:
public class User
@VersioningProperties(
@Property(version = "1.5", value = "fname"),
@Property(version = "1.6", value = "firstName")
)
private String firstName;
// constructors, getters and setters
以及将ObjectMapper
与内省器一起使用的示例:
User user = new User();
user.setFirstName("catherine");
user.setVersion("1.5");
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new VersioningPropertiesIntrospector(user.getVersion()));
String userJson = mapper.writeValueAsString(user);
User userRead = mapper.readValue(userJson, User.class);
您也可以考虑实现一个工厂,通过传递版本信息来获取ObjectMapper
。
【讨论】:
以上是关于使用 Jackson 根据 API 版本指定不同的 JSON 属性名称的主要内容,如果未能解决你的问题,请参考以下文章
WebView:根据 Android API 版本不同的 css 解释