content: 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;
          }
      }
  }
  