Support specifying method for instantiating builders in JsonDeserialize
See original GitHub issueI request adding a parameter such as builderMethod
to JsonDeserialize
(e.g. @JsonDeserialize(builderMethod = "builder")
) to facilitate a way of specifying a method to instantiate a builder instance.
Example use case When using the Lombok SuperBuilder annotation it results in the creation two builder inner classes, one public and the other private which subclasses the public; it is the private class that must be instantiated. Currently when using the SuperBuilder annotation it is necessary to declare the private inner class (which will be completed during the annotation processing stage) to set its access modifier to package private so that Jackson can instantiated it.
An example original source file and the annotation processed result follows:
Original file
@JsonDeserialize(builder = LegalDocument.LegalDocumentBuilderImpl.class)
@Getter
@SuperBuilder(toBuilder = true)
public final class LegalDocument {
private final String code;
private final String fileName;
private final String description;
// This should not be necessary
static final class LegalDocumentBuilderImpl
extends LegalDocument.LegalDocumentBuilder<
LegalDocument, LegalDocumentBuilderImpl> {
}
}
After annotation processing
@JsonDeserialize(
builder = LegalDocument.LegalDocumentBuilderImpl.class
)
public final class LegalDocument {
private final String code;
private final String fileName;
private final String description;
@Generated
protected LegalDocument(LegalDocument.LegalDocumentBuilder<?, ?> b) {
this.code = b.code;
this.fileName = b.fileName;
this.description = b.description;
}
// I would like to be able reference this method in the JsonDerserialize annotation
@Generated
public static LegalDocument.LegalDocumentBuilder<?, ?> builder() {
return new LegalDocument.LegalDocumentBuilderImpl();
}
@Generated
public LegalDocument.LegalDocumentBuilder<?, ?> toBuilder() {
return (new LegalDocument.LegalDocumentBuilderImpl()).$fillValuesFrom(this);
}
@Generated
public String getCode() {
return this.code;
}
@Generated
public String getFileName() {
return this.fileName;
}
@Generated
public String getDescription() {
return this.description;
}
@Generated
public abstract static class LegalDocumentBuilder<C extends LegalDocument, B extends LegalDocument.LegalDocumentBuilder<C, B>> {
@Generated
private String code;
@Generated
private String fileName;
@Generated
private String description;
public LegalDocumentBuilder() {
}
@Generated
protected B $fillValuesFrom(C instance) {
$fillValuesFromInstanceIntoBuilder(instance, this);
return this.self();
}
@Generated
private static void $fillValuesFromInstanceIntoBuilder(LegalDocument instance, LegalDocument.LegalDocumentBuilder<?, ?> b) {
b.code(instance.code);
b.fileName(instance.fileName);
b.description(instance.description);
}
@Generated
protected abstract B self();
@Generated
public abstract C build();
@Generated
public B code(String code) {
this.code = code;
return this.self();
}
@Generated
public B fileName(String fileName) {
this.fileName = fileName;
return this.self();
}
@Generated
public B description(String description) {
this.description = description;
return this.self();
}
@Generated
public String toString() {
return "LegalDocument.LegalDocumentBuilder(code=" + this.code + ", fileName=" + this.fileName + ", description=" + this.description + ")";
}
}
static final class LegalDocumentBuilderImpl extends LegalDocument.LegalDocumentBuilder<LegalDocument, LegalDocument.LegalDocumentBuilderImpl> {
@Generated
private LegalDocumentBuilderImpl() {
}
@Generated
protected LegalDocument.LegalDocumentBuilderImpl self() {
return this;
}
@Generated
public LegalDocument build() {
return new LegalDocument(this);
}
}
}
Issue Analytics
- State:
- Created 4 years ago
- Reactions:4
- Comments:11 (5 by maintainers)
Top GitHub Comments
Ah, interesting. My interest in test was more wrt actual dynamic access, but then again with CI system it could catch changes to ability compile too (should warnings become errors).
Either way, this is useful information as it makes the case for addition of method stronger. I’ll have to see if this would be doable for 2.11, given that it requires annotation addition (but to annotation that is in databind, one of few)
Here is the relevant part of the JLS (section 6.6.1.):
The important part is “within the body of the top level type”. As the annotation is placed outside the body of the outer class, javac is correct to complain that the nested private class is not accessible. If this compiles in javac 8 (I can’t verify that right now), I’m quite sure the behavior of javac 8 would be incorrect (and that javac 9 complains is more likely a bugfix than a result of the module system).
The test you introduced in b448a2a only compiles because you have a top-level type around the nested class that has the annotation. Thus, the annotation is within the body of the top-level type. IMO the JLS is not very consistent at this point (my guess is that this definition existed before generics and annotations were introduced). However, this is not the typical case: Most builders are for top-level types, not nested types.