Skip to content

Commit

Permalink
support jackson annotation JsonDeserialize#builder, for issue #2399
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed Apr 6, 2024
1 parent 46c5241 commit 2412835
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ private void processJacksonJsonDeserializer(BeanInfo beanInfo, Annotation annota
if (using != null) {
beanInfo.deserializer = using;
}
} else if ("builder".equals(name)) {
processBuilder(beanInfo, (Class) result);
}
} catch (Throwable ignored) {
// ignored
Expand Down Expand Up @@ -512,37 +514,7 @@ void getBeanInfo1x(BeanInfo beanInfo, Annotation annotation) {
break;
}
case "builder": {
Class<?> builderClass = (Class) result;
if (builderClass != void.class && builderClass != Void.class) {
beanInfo.builder = builderClass;

for (Annotation builderAnnotation : getAnnotations(builderClass)) {
Class<? extends Annotation> builderAnnotationClass = builderAnnotation.annotationType();
String builderAnnotationName = builderAnnotationClass.getName();

if ("com.alibaba.fastjson.annotation.JSONPOJOBuilder".equals(builderAnnotationName)) {
getBeanInfo1xJSONPOJOBuilder(beanInfo, builderClass, builderAnnotation, builderAnnotationClass);
} else {
JSONBuilder jsonBuilder = findAnnotation(builderClass, JSONBuilder.class);
if (jsonBuilder != null) {
String buildMethodName = jsonBuilder.buildMethod();
beanInfo.buildMethod = buildMethod(builderClass, buildMethodName);
String withPrefix = jsonBuilder.withPrefix();
if (!withPrefix.isEmpty()) {
beanInfo.builderWithPrefix = withPrefix;
}
}
}
}

if (beanInfo.buildMethod == null) {
beanInfo.buildMethod = BeanUtils.buildMethod(builderClass, "build");
}

if (beanInfo.buildMethod == null) {
beanInfo.buildMethod = BeanUtils.buildMethod(builderClass, "create");
}
}
processBuilder(beanInfo, (Class) result);
break;
}
case "deserializeUsing": {
Expand All @@ -568,6 +540,41 @@ void getBeanInfo1x(BeanInfo beanInfo, Annotation annotation) {
});
}

private void processBuilder(BeanInfo beanInfo, Class result) {
Class<?> builderClass = result;
if (builderClass != void.class && builderClass != Void.class) {
beanInfo.builder = builderClass;

for (Annotation builderAnnotation : getAnnotations(builderClass)) {
Class<? extends Annotation> builderAnnotationClass = builderAnnotation.annotationType();
String builderAnnotationName = builderAnnotationClass.getName();

if ("com.alibaba.fastjson.annotation.JSONPOJOBuilder".equals(builderAnnotationName)
|| "com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder".equals(builderAnnotationName)) {
getBeanInfo1xJSONPOJOBuilder(beanInfo, builderClass, builderAnnotation, builderAnnotationClass);
} else {
JSONBuilder jsonBuilder = findAnnotation(builderClass, JSONBuilder.class);
if (jsonBuilder != null) {
String buildMethodName = jsonBuilder.buildMethod();
beanInfo.buildMethod = buildMethod(builderClass, buildMethodName);
String withPrefix = jsonBuilder.withPrefix();
if (!withPrefix.isEmpty()) {
beanInfo.builderWithPrefix = withPrefix;
}
}
}
}

if (beanInfo.buildMethod == null) {
beanInfo.buildMethod = BeanUtils.buildMethod(builderClass, "build");
}

if (beanInfo.buildMethod == null) {
beanInfo.buildMethod = BeanUtils.buildMethod(builderClass, "create");
}
}
}

private void processSeeAlsoAnnotation(BeanInfo beanInfo, Class<?> objectClass) {
Class mixInSource = provider.mixInCache.get(objectClass);
if (mixInSource == null) {
Expand Down Expand Up @@ -1240,14 +1247,17 @@ private void getFieldInfo(FieldInfo fieldInfo, JSONField jsonField) {
}
}

private void getBeanInfo1xJSONPOJOBuilder(BeanInfo beanInfo,
Class<?> builderClass,
Annotation builderAnnatation,
Class<? extends Annotation> builderAnnatationClass) {
private void getBeanInfo1xJSONPOJOBuilder(
BeanInfo beanInfo,
Class<?> builderClass,
Annotation builderAnnatation,
Class<? extends Annotation> builderAnnatationClass
) {
BeanUtils.annotationMethods(builderAnnatationClass, method -> {
try {
String methodName = method.getName();
switch (methodName) {
case "buildMethodName":
case "buildMethod": {
String buildMethodName = (String) method.invoke(builderAnnatation);
beanInfo.buildMethod = BeanUtils.buildMethod(builderClass, buildMethodName);
Expand Down
155 changes: 155 additions & 0 deletions core/src/test/java/com/alibaba/fastjson2/issues_2400/Issue2399.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package com.alibaba.fastjson2.issues_2400;

import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.NonNull;
import org.junit.jupiter.api.Test;

import java.io.Serializable;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class Issue2399 {
@Test
public void test() {
User user = new User.UserBuilderImpl().id(123L).firstName("wenshao").build();
String str = JSON.toJSONString(user);
assertEquals("{\"first_name\":\"wenshao\",\"id\":123}", str);
System.out.println(str);
User user1 = JSON.parseObject(str, User.class);
assertEquals(user.id, user1.id);
assertEquals(user.firstName, user1.firstName);
}

@JsonIgnoreProperties(
ignoreUnknown = true
)
@JsonInclude(JsonInclude.Include.NON_NULL)
public interface ApiObject
extends Serializable {
}

@JsonDeserialize(builder = User.UserBuilderImpl.class)
public static class User
implements ApiObject {
private static final String ID_FIELD = "id";
private static final String FIRST_NAME_FIELD = "first_name";
@JsonProperty("id")
private @NonNull Long id;
@JsonProperty("first_name")
private @NonNull String firstName;

protected User(User.UserBuilder<?, ?> b) {
this.id = b.id;
if (this.id == null) {
throw new NullPointerException("id is marked non-null but is null");
} else {
this.firstName = b.firstName;
if (this.firstName == null) {
throw new NullPointerException("firstName is marked non-null but is null");
}
}
}

public static User.UserBuilder<?, ?> builder() {
return new User.UserBuilderImpl();
}

public @NonNull Long getId() {
return this.id;
}

public @NonNull String getFirstName() {
return this.firstName;
}

@JsonProperty("id")
public void setId(@NonNull Long id) {
if (id == null) {
throw new NullPointerException("id is marked non-null but is null");
} else {
this.id = id;
}
}

@JsonProperty("first_name")
public void setFirstName(@NonNull String firstName) {
if (firstName == null) {
throw new NullPointerException("firstName is marked non-null but is null");
} else {
this.firstName = firstName;
}
}

public String toString() {
Long var10000 = this.getId();
return "User(id=" + var10000 + ", firstName=" + this.getFirstName() + ")";
}

public User(@NonNull Long id, @NonNull String firstName) {
if (id == null) {
throw new NullPointerException("id is marked non-null but is null");
} else if (firstName == null) {
throw new NullPointerException("firstName is marked non-null but is null");
}
}

public abstract static class UserBuilder<C extends User, B extends User.UserBuilder<C, B>> {
private Long id;
private String firstName;

public UserBuilder() {
}

@JsonProperty("id")
public B id(@NonNull Long id) {
if (id == null) {
throw new NullPointerException("id is marked non-null but is null");
} else {
this.id = id;
return this.self();
}
}

@JsonProperty("first_name")
public B firstName(@NonNull String firstName) {
if (firstName == null) {
throw new NullPointerException("firstName is marked non-null but is null");
} else {
this.firstName = firstName;
return this.self();
}
}

protected abstract B self();

public abstract C build();

public String toString() {
return "User.UserBuilder(id=" + this.id + ", firstName=" + this.firstName + ")";
}
}

@JsonPOJOBuilder(
withPrefix = "",
buildMethodName = "build"
)
static final class UserBuilderImpl
extends User.UserBuilder<User, User.UserBuilderImpl> {
private UserBuilderImpl() {
}

protected User.UserBuilderImpl self() {
return this;
}

public User build() {
return new User(this);
}
}
}
}

0 comments on commit 2412835

Please sign in to comment.