繁体   English   中英

根据与Jackson的API版本指定不同的JSON属性名称

[英]Specifying different JSON property names according to API version with Jackson

我需要能够使用Jackson同时支持多个API版本来序列化/反序列化对象。 我已经探索过如下解决方案:

  • @JsonProperty
  • PropertyNamingStrategy
  • Mixin注释

但是,每一个都会导致自己的问题。 @JsonProperty是一个完美的解决方案,如果我可以直接在注释中添加具有正确名称的多个版本:

@JsonProperty(api="1.5", "fname")
@JsonProperty(api="1.6", "firstname")
String firstName;

随着时间的推移,可能会有很长的时间,但它肯定很容易理解。 但是,这似乎不太可能。

PropertyNamingStrategy和mixins也是一个好主意。 事实上,我尝试使用mixin注释(例如, 具有不同JSON属性名称的Inherit模型 )并且它们起作用,但是这两种解决方案都遇到了一个问题。 你必须在某处指定和使用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 )。 这有几个问题:

  • 每次版本号上升时,您都必须添加另一个if块
  • 这只处理基类。 如果子类需要更改一些属性怎么办? 如果只有少数几个类,那不是问题,但很快就会失控。
  • 更重要的是,将映射器/读取器/写入器设置为正确的类来映射将是一个巨大的痛苦和错误的来源,因为有人会不可避免地忘记这样做。 此外,将mapper,reader或writer放在类本身中只会导致堆栈溢出或无限递归。
  • 最后,根据API版本设置正确的类,并且必须手动完成被映射的类,除非我遗漏了一些东西。 反思可以解决这个问题
    • a)它很慢,这种东西会经常被称为使用它和
    • b)如果它在父类中使用,它将无法看到子类,因此强制您在子类本身中使用反射来设置映射器。

理想情况下,我希望能够使用类似上面的@JsonProperty示例,因为名称更改是问题,而不是我对变量所做的事情。 我也考虑过使用

@JsonSerialize(using = EntitySerializer.class)
@JsonDeserialize(using = EntityDeserializer.class)

注释,但这只会改变变量的内容,而不是属性名称本身。

您可以创建自定义注释以定义字段的可能版本的属性名称列表,并创建自定义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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM