Specifying different JSON property names according to API version with Jackson

I need to be able to support multiple versions of the API at the same time using Jackson to serialize / deserialize objects. I have studied solutions such as:

  • @JsonProperty
  • PropertyNamingStrategy
  • Mixin annotations

However, each of them causes its own problems. @JsonProperty would be the perfect solution if I could add, say, several versions with the correct names directly in the annotation:

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

Over time, this can become big, but it will certainly be easy to understand. However, this does not seem to be possible.

PropertyNamingStrategy and mixins would also be a good idea. In fact, I tried mixin annotations (for example, Inherit a model with different JSON property names ) and they worked, but both of these solutions suffer from the same problem. You should specify and use ObjectMapper (and perhaps ObjectReader / Writer too) somewhere.

This is a pain because the hierarchy of objects looks like this:

An object

| --user

| --group

| --Credential

etc .. Entity contains common properties such as name, identifier, description, status and version of the API. Let's say now you do the following:

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));

where MAPPERis just an ObjectMapper defined elsewhere, but EntityMixinV2is something like:

public abstract class EntityMixinV2 {

    @JsonProperty("employmentState")
    String state;
}

to override one of the variables (in this case state) in the user's parent class, Entity. There are several issues with this:

  • , , ,
  • . , ? , , - .
  • , // , - . , , .
  • , API, , -. ,
    • a) , ,
    • b) , , , .

- @JsonProperty, - , , .

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

, .

+4
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);

factory ObjectMapper .

+2

Source: https://habr.com/ru/post/1649476/


All Articles