Skip to content

Commit

Permalink
support jackson annotation JsonTypeInfo on interface, for issue #2548
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed May 11, 2024
1 parent 77c1c44 commit ec566bd
Show file tree
Hide file tree
Showing 2 changed files with 267 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.alibaba.fastjson2.util.*;

import java.io.File;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.math.BigDecimal;
Expand Down Expand Up @@ -57,16 +58,24 @@ public void getBeanInfo(BeanInfo beanInfo, Class objectClass) {
Class superclass = objectClass.getSuperclass();
if (superclass != Object.class && superclass != null && superclass != Enum.class) {
getBeanInfo(beanInfo, superclass);
}

Class[] interfaces = objectClass.getInterfaces();
for (Class item : interfaces) {
if (item == Serializable.class) {
continue;
}
getBeanInfo(beanInfo, item);
}

if (beanInfo.seeAlso != null && beanInfo.seeAlsoNames != null) {
for (int i = 0; i < beanInfo.seeAlso.length; i++) {
Class seeAlso = beanInfo.seeAlso[i];
if (seeAlso == objectClass && i < beanInfo.seeAlsoNames.length) {
String seeAlsoName = beanInfo.seeAlsoNames[i];
if (seeAlsoName != null && seeAlsoName.length() != 0) {
beanInfo.typeName = seeAlsoName;
break;
}
if (beanInfo.seeAlso != null && beanInfo.seeAlsoNames != null) {
for (int i = 0; i < beanInfo.seeAlso.length; i++) {
Class seeAlso = beanInfo.seeAlso[i];
if (seeAlso == objectClass && i < beanInfo.seeAlsoNames.length) {
String seeAlsoName = beanInfo.seeAlsoNames[i];
if (seeAlsoName != null && seeAlsoName.length() != 0) {
beanInfo.typeName = seeAlsoName;
break;
}
}
}
Expand Down
249 changes: 249 additions & 0 deletions core/src/test/java/com/alibaba/fastjson2/issues_2500/Issue2548A.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
package com.alibaba.fastjson2.issues_2500;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Objects;

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

public class Issue2548A {
@Test
public void fastJson() {
StoreDTO storeDTO = new StoreDTO();
BananaDTO banana = new BananaDTO();
banana.setCount(BigDecimal.valueOf(10));
storeDTO.setItem(banana);

String jsonString = JSON.toJSONString(storeDTO, JSONWriter.Feature.WriteNulls);
assertEquals("{\"item\":{\"kind\":\"banana\",\"count\":10},\"storeId\":null}", jsonString);

StoreDTO store = JSON.parseObject(jsonString, StoreDTO.class);

assertEquals("banana", store.getItem().getKind());
}

@Test
public void jackson() throws Exception {
//given
ObjectMapper objectMapper = new ObjectMapper();

StoreDTO storeDTO = new StoreDTO();
BananaDTO banana = new BananaDTO();
banana.setCount(BigDecimal.valueOf(10));
storeDTO.setItem(banana);

//when
String jsonString = objectMapper.writeValueAsString(storeDTO);
StoreDTO store = objectMapper.readValue(jsonString, StoreDTO.class);

//then
assertEquals("banana", store.getItem().getKind());
}

@JsonTypeName("store")
public static class StoreDTO
implements Serializable {
private static final long serialVersionUID = 1L;

private String storeId;

private FruitDTO item;

public StoreDTO storeId(String storeId) {
this.storeId = storeId;
return this;
}

/**
* The unique ID of the fruit
* @return storeId
*/

@JsonProperty("storeId")
public String getStoreId() {
return storeId;
}

public void setStoreId(String storeId) {
this.storeId = storeId;
}

public StoreDTO item(FruitDTO item) {
this.item = item;
return this;
}

/**
* Get item
* @return item
*/
@JsonProperty("item")
public FruitDTO getItem() {
return item;
}

public void setItem(FruitDTO item) {
this.item = item;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
StoreDTO store = (StoreDTO) o;
return Objects.equals(this.storeId, store.storeId) &&
Objects.equals(this.item, store.item);
}

@Override
public int hashCode() {
return Objects.hash(storeId, item);
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class StoreDTO {\n");
sb.append(" storeId: ").append(toIndentedString(storeId)).append("\n");
sb.append(" item: ").append(toIndentedString(item)).append("\n");
sb.append("}");
return sb.toString();
}

/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
}

@JsonIgnoreProperties(
value = "kind", // ignore manually set kind, it will be automatically generated by Jackson during serialization
allowSetters = true // allows the kind to be set during deserialization
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "kind", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = AppleDTO.class, name = "apple"),
@JsonSubTypes.Type(value = BananaDTO.class, name = "banana")
})
public interface FruitDTO
extends Serializable {
public String getKind();
}

@JsonTypeName("banana")
public static class BananaDTO
implements Serializable, FruitDTO {
private static final long serialVersionUID = 1L;

private String kind;

private BigDecimal count;

public BananaDTO kind(String kind) {
this.kind = kind;
return this;
}

/**
* Get kind
* @return kind
*/

@JsonProperty("kind")
public String getKind() {
return kind;
}

public void setKind(String kind) {
this.kind = kind;
}

public BananaDTO count(BigDecimal count) {
this.count = count;
return this;
}

/**
* Get count
* @return count
*/
@JsonProperty("count")
public BigDecimal getCount() {
return count;
}

public void setCount(BigDecimal count) {
this.count = count;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BananaDTO banana = (BananaDTO) o;
return Objects.equals(this.kind, banana.kind) &&
Objects.equals(this.count, banana.count);
}

@Override
public int hashCode() {
return Objects.hash(kind, count);
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class BananaDTO {\n");
sb.append(" kind: ").append(toIndentedString(kind)).append("\n");
sb.append(" count: ").append(toIndentedString(count)).append("\n");
sb.append("}");
return sb.toString();
}

/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
}

@JsonTypeName("apple")
public static class AppleDTO
implements Serializable, FruitDTO {
private String kind;

@Override
public String getKind() {
return kind;
}

public void setKind(String kind) {
this.kind = kind;
}
}
}

0 comments on commit ec566bd

Please sign in to comment.