我需要能够使用Jackson同时支持多个API版本来序列化/反序列化对象。我已经探索过如下解决方案:
然而,每一个都会导致自己的问题。 @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)。
这很痛苦,因为对象的层次结构如下所示:
实体
| --User
| --group
| --Credential
等。实体包含公共属性,例如名称,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)
注释,但这只会改变变量的内容,而不是属性名称本身。
答案 0 :(得分:2)
您可以创建自定义注释以定义字段的可能版本的属性名称列表,并创建自定义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
与introspector一起使用的示例:
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
。