Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lombok @SuperBuilder example with json annotations

Tags:

java

lombok

can someone please provide me a working example with the lombok @SuperBuilder experimental annotation?

I can't get it running and there is no code example as documentation available.

Currently my code looks like this:

Superclass:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = SubA.class),
  @JsonSubTypes.Type(value = AnotherSub.class)
})
@Getter
@Accessors(fluent = true, chain = true)
@SuperBuilder
public abstract class AbstractA {

    @JsonProperty
    protected final String superProperty;
}

And the subclass:

@Getter
@EqualsAndHashCode(callSuper = true)
@Accessors(fluent = true, chain = true)
@SuperBuilder
@JsonDeserialize(builder = SubA.SubABuilder.class) // class not found?
@JsonTypeName("SubA")
public class SubA extends AbstractA {

    @JsonProperty
    private final String fieldA;
}

Thanks

like image 542
Sebastian Thees Avatar asked Oct 10 '18 06:10

Sebastian Thees


People also ask

What is @SuperBuilder Lombok?

@SuperBuilder was introduced as experimental feature in lombok v1. 18.2. @SuperBuilder 's toBuilder feature and limited support for customization was added with lombok v1. 18.4. @SuperBuilder customization possibilities were extended with lombok v1.

What does @jacksonized annotation do?

The @Jacksonized annotation is an add-on annotation for @Builder and @SuperBuilder . It automatically configures the generated builder class to be used by Jackson's deserialization. It only has an effect if present at a context where there is also a @Builder or a @SuperBuilder ; a warning is emitted otherwise.

Does @builder require AllArgsConstructor?

Martin Grajcar. Your @Builder needs an @AllArgsConstructor (add it; feel free to make it private). You received this message because you are subscribed to the Google Groups "Project Lombok" group.

Is SuperBuilder still experimental?

Solving this problem is probably possible but seems at first glance quite costly compared to the noise it aims to kill. Especially knowing that “SuperBuilder” is still considered as an experimental feature.


2 Answers

Updated 2018-11-10: Lombok 1.18.4 released

Updated 2020-10-18: Lombok 1.18.16 released

Lombok 1.18.16 contains the new @Jacksonized annotation. With it, you can simply write:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = SubA.class),
  @JsonSubTypes.Type(value = AnotherSub.class)
})
@Jacksonized
@SuperBuilder
// more annotations...
public abstract class AbstractA {

    @JsonProperty
    protected final String superProperty;
}

@Jacksonized
@SuperBuilder
@JsonTypeName("SubA")
// more annotations...
public class SubA extends AbstractA {

    @JsonProperty
    private final String fieldA;
}

This will automatically insert all necessary Jackson annotations and do some adjustments to the generated builder. No need to customized anymore.

For earlier Lombok versions between 1.18.4 and 1.18.12, this is the way to go:

For Lombok's @Builder and @SuperBuilder to work with Jackson, you have to add the builder class header manually and place a @JsonPOJOBuilder(withPrefix="") on it. Lombok will then generate only the remainder of the builder class. This is necessary because Jackson's default is that the builder's setter methods have "with" as prefix, but Lombok's builders don't have any prefix (and Lombok is not and will probably never be configurable in this regard).

When @SuperBuilder was introduced in Lombok 1.18.2, it was not customizable (i.e., you could not manually add the builder class header). As a result, using @SuperBuilder with Jackson was not easily possible.

This changed with Lombok 1.18.4 (see this pull request): @SuperBuilder is now (at least partially) customizable, and this allows us to add the annotation. Beware that the code generated by @SuperBuilder is quite complex and heavily loaded with generics. To avoid accidentally messing up the code, you should have a look at the delombok output and copy/paste the class header from there. Here, you need add the builder implementation class header and put the annotation on it:

@JsonPOJOBuilder(withPrefix="")
static final class SubABuilderImpl extends SubABuilder<SubA, SubABuilderImpl> {
}

Note that you have to broaden the visibility of SubABuilderImpl to at least package-private.

The @JsonDeserialize annotation must also refer to the builder implementation class, not the abstract builder:

@JsonDeserialize(builder = SubA.SubABuilderImpl.class)
like image 153
Jan Rieke Avatar answered Oct 18 '22 19:10

Jan Rieke


A working solution in Eclipse, note that the Lombok IntelliJ integration is not supporting all features, therefore the code compiles fine in Eclipse and with javac but IntelliJ thinks it's broken but executes the code without an issue.

public static ObjectMapper createObjectMapper() {

    final ObjectMapper mapper = new ObjectMapper();
    mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {

        @Override
        public JsonPOJOBuilder.Value findPOJOBuilderConfig(final AnnotatedClass ac) {
            if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
                return super.findPOJOBuilderConfig(ac);
            }
            return new JsonPOJOBuilder.Value("build", "");
        }
    });

    return mapper;
}

public static void main(final String[] args) throws Exception {
    final ObjectMapper objectMapper = createObjectMapper();

    final String serializedForm = objectMapper.writeValueAsString(SubA.builder().build());
    System.out.println(serializedForm);
    final SubA anA = objectMapper.readValue(serializedForm, SubA.class);
    System.out.println(anA);
}


@Getter
@EqualsAndHashCode(callSuper = true)
@Accessors(fluent = true, chain = true)
@SuperBuilder
@JsonDeserialize(builder = SubA.SubABuilderImpl.class)
@JsonTypeName("SubA")
public static class SubA extends AbstractA {

    @JsonProperty
    private final String fieldA;
}


@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = SubA.class)
})
@Getter
@Accessors(fluent = true, chain = true)
@SuperBuilder
public static abstract class AbstractA {

    @JsonProperty
    protected final String superProperty;
}
like image 26
Andreas Avatar answered Oct 18 '22 19:10

Andreas