jackson-future-ideas icon indicating copy to clipboard operation
jackson-future-ideas copied to clipboard

Group-based approach to context-based serialization

Open mjustin opened this issue 4 years ago • 1 comments
trafficstars

Is your feature request related to a problem? Please describe. Every so often, I have a desire to de/serialize the same object differently in different contexts. This can be accomplished today by writing different mixins for each context. Alternatively, if it's just a difference of field inclusion, the @JsonView annotation can be used.

On thing that feels clunky with the mixin approach is that the contextual configuration differences are declared in two different code locations. To figure out how any given field might be serialized, both locations need to be viewed and compared.

Mixins also have to be declared at ObjectMapper creation time, whereas views can be varied per request:

objectMapper.writerWithView(MyView.class).writeValueAsString(myValue);
objectMapper.readerWithView(MyView.class).readValue(json, MyType.class);

objectMapper.copy().addMixIn(MyType.class, MyMixin.class).writeValueAsString(myValue);
objectMapper.copy().addMixIn(MyType.class, MyMixin.class).readValue(json, MyType.class);

Mixins are difficult to use per-request in the Spring Web framework since it generally assumes a single ObjectMapper and it requires quite a bit of non-obvious cumbersome configuration to support different ObjectMappers for different endpoints. On the other hand, JSON views have first class support:

@RestController
public class UserController {
    @GetMapping("/user")
    @JsonView(User.WithoutPasswordView.class)
    public User getUser() {
        return new User("eric", "7!jd#h23");
    }
}

In summary, while powerful, mixins have a few strikes against them in the usability department. JSON views, on the other hand, only work for property exclusion, and cannot be used for other kinds of serialization differences.

Describe the solution you'd like It would seem like a group-based mechanism like JsonView that would work for any Jackson configuration annotation would be handy, and would fit well with Jackson, as an alternative to using mixins.

Other annotation-based libraries in the Java ecosystem do similar. The main one that comes to mind is Jakarta Bean Validation with its validation groups:

/** Validates a minimal set of constraints */
public interface Minimal {}

public class Address {

    @NonEmpty(groups = Minimal.class)
    @Size(max=50)
    private String street1;

    @NonEmpty
    private String city;

    @NonEmpty(groups = {Minimal.class, Default.class})
    private String zipCode;

    [...]
}

Usage example One option would be to add a groups (or group) element to literally every annotation type like Jakarta Bean Validation does, in which case it would look very similar to the above Jakarta Bean Validation example:

public class Address {
    @JsonFormat(pattern = "MM/dd/yyyy", groups = ExternalApi1.class)
    @JsonFormat(shape = NUMBER, groups = ExternalApi2.class)
    @JsonFormat(pattern = "yyyy-MM-dd") // The default if no group
    private LocalDate myField;
}

If you didn't want to add groups to every single Jackson annotation, an alternate approach might be to put the annotations inside a dedicated grouping annotation:

@JsonGroup(group = ExternalApi1.class, annotations = {@JsonFormat(pattern = "MM/dd/yyyy")})
@JsonGroup(group = ExternalApi2.class, annotations = {@JsonFormat(shape = NUMBER)})
@JsonFormat(pattern = "yyyy-MM-dd") // The default if no group
private LocalDate myField;

mjustin avatar Mar 12 '21 22:03 mjustin

I'll move this to more appropriate place, in case someone might find it interesting and wanted to pursue it.

cowtowncoder avatar Apr 15 '21 01:04 cowtowncoder