java 生成的子类的反序列化在父对象中产生

zysjyyx4  于 2023-08-02  发布在  Java
关注(0)|答案(2)|浏览(66)
  • 版本:Swagger-codegen(v3):3.0.11|Jackson:2.9.8*

我目前从一个swagger.yaml文件生成我的类(由于双重复制-粘贴,缩进可能会有所不同):

---
swagger: "2.0"
info:
  description: "servicename"
  version: "1.0"
  title: "servicename"
schemes:
  - "http"
  - "https"
paths:

definitions:
  Notification:
    type: "object"
    discriminator: "typeOfNotification"
    properties:
      typeOfNotification:
        description: "Type of notification"
        enum:
          - "EMAIL"
          - "SMS"
        default: "EMAIL"
  EmailNotification:
    allOf:
    - $ref: "#/definitions/Notification"
    - type: "object"
      properties:
        name:
          type: "string"
          example: "Name of reciever"
          description: "Name of the receiver"
        emailAddress:
          type: "string"
          example: "info@stackoverflow.com"
          description: "Email address of the receiver"

字符串
它生成两个类:
Notification.java

package nl.test;

import java.util.Objects;
import java.util.Arrays;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.IOException;
/**
 * Notification
 */

@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2019-09-26T16:01:12.401+02:00[Europe/Amsterdam]")
public abstract class Notification {
  /**
   * Type of notification
   */
  @JsonAdapter(TypeOfNotificationEnum.Adapter.class)
  public enum TypeOfNotificationEnum {
    EMAIL("EMAIL"),
    SMS("SMS");

    private String value;

    TypeOfNotificationEnum(String value) {
      this.value = value;
    }
    public String getValue() {
      return value;
    }

    @Override
    public String toString() {
      return String.valueOf(value);
    }
    public static TypeOfNotificationEnum fromValue(String text) {
      for (TypeOfNotificationEnum b : TypeOfNotificationEnum.values()) {
        if (String.valueOf(b.value).equals(text)) {
          return b;
        }
      }
      return null;
    }
    public static class Adapter extends TypeAdapter<TypeOfNotificationEnum> {
      @Override
      public void write(final JsonWriter jsonWriter, final TypeOfNotificationEnum enumeration) throws IOException {
        jsonWriter.value(enumeration.getValue());
      }

      @Override
      public TypeOfNotificationEnum read(final JsonReader jsonReader) throws IOException {
        String value = jsonReader.nextString();
        return TypeOfNotificationEnum.fromValue(String.valueOf(value));
      }
    }
  }  @SerializedName("typeOfNotification")
  private TypeOfNotificationEnum typeOfNotification = null;

  public Notification typeOfNotification(TypeOfNotificationEnum typeOfNotification) {
    this.typeOfNotification = typeOfNotification;
    return this;
  }

  @Schema(description = "Type of notification")
  public TypeOfNotificationEnum getTypeOfNotification() {
    return typeOfNotification;
  }

  public void setTypeOfNotification(TypeOfNotificationEnum typeOfNotification) {
    this.typeOfNotification = typeOfNotification;
  }

  @Override
  public boolean equals(java.lang.Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    Notification notification = (Notification) o;
    return Objects.equals(this.typeOfNotification, notification.typeOfNotification);
  }

  @Override
  public int hashCode() {
    return Objects.hash(typeOfNotification);
  }
}


以及EmailNotification:

package nl.test;

import java.util.Objects;
import java.util.Arrays;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.IOException;
import nl.test.Notification;
/**
 * EmailNotification
 */

@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2019-09-26T16:01:12.401+02:00[Europe/Amsterdam]")
public class EmailNotification extends Notification {
  @SerializedName("name")
  private String name = null;

  @SerializedName("emailAddress")
  private String emailAddress = null;

  public EmailNotification name(String name) {
    this.name = name;
    return this;
  }

   /**
   * Name of the receiver
   * @return name
  **/
  @Schema(example = "Name of receiver", description = "Name of the receiver")
  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public EmailNotification emailAddress(String emailAddress) {
    this.emailAddress = emailAddress;
    return this;
  }

   /**
   * Email address of the receiver
   * @return emailAddress
  **/
  @Schema(example = "test@stackoverflow.com", description = "Email address of the receiver")
  public String getEmailAddress() {
    return emailAddress;
  }

  public void setEmailAddress(String emailAddress) {
    this.emailAddress = emailAddress;
  }

  @Override
  public boolean equals(java.lang.Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
     EmailNotification = (EmailNotification) o;
    return Objects.equals(this.name, emailNotification.name) &&
        Objects.equals(this.emailAddress, emailNotification.emailAddress) &&
        super.equals(o);
  }

  @Override
  public int hashCode() {
    return Objects.hash(name, emailAddress, super.hashCode());
  }

}


这很好但是现在问题发生在JSON的反序列化过程中。当ObjectMapper传递对应于User类的JSON时,它有一个Notification类型的属性,而JSON实际上是EmailNotification类型,那么由于某种原因,反序列化器不关心并将其反序列化为Notification,从而忽略emailAddress属性。
对应的测试类别:

package nl.test;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.threeten.bp.LocalDate;

import static com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS;
import static org.junit.Assert.*;

@RunWith(MockitoJUnitRunner.class)
public class ObjectMappersConfigurationTest {

    @Test
    public void test() throws Exception {
        String json = "{\n" +
                "  \"user\": {\n" +
                "    \"notification\": {\n" +
                "      \"name\": \"somename\",\n" +
                "      \"emailAddress\": \"test@stackoverflow.com\",\n" +
                "      \"typeOfNotification\": \"EMAIL\"\n" +
                "    },\n" +
                "  }\n" +
                "}\n";

        ObjectMapper mapper = createJsonObjectMapper();
        User order = mapper.readValue(json, User.class);

        assertEquals(1, 1);
    }

    public ObjectMapper createJsonObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        setDefaultRelaxObjectMappings(objectMapper);
        return objectMapper;
    }

    public static void setDefaultRelaxObjectMappings(ObjectMapper objectMapper) {
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);

        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.enable(ACCEPT_CASE_INSENSITIVE_ENUMS);
    }
}


输出将是:

User {
  notification {
     notificationType: EMAIL
  }
}


在那里我期望:

User {
  notification {
     notificationType: EMAIL,
     emailAddress: "...",
     name: "..."
  }
}


现在我知道我可以使用自定义的反序列化器来实现这个目的,但这似乎有点矫枉过正,我想用继承来反序列化对象应该是一个简单的设置。我就是找不到。
启用默认类型将是一个替代方案,但我宁愿避免,因为我们的YAML文件来自外部方,迫使他们默认类型的YAML不是最好的解决方案IMO。

vof42yt1

vof42yt11#

结果发现缺少@JsonSubType注解。由于ObjectMapper不与swagger通信,它永远不知道类应该Map到EmailNotification,因此总是创建Notification对象。
添加:

<plugin>
   ....
   <configuration>
     ...
     <library>jersey2</library>
   </configuration>
</plugin>

字符串
修复了它,Notification类的类注解现在生成为:

@JsonSubTypes({
  @JsonSubTypes.Type(value = SMSNotification.class, name = "SMSNotification"),
  @JsonSubTypes.Type(value = EmailNotification.class, name = "EmailNotification"),
})
public class Notification {
    ....


这增加了ObjectMapper在反序列化时理解的Map。
特别感谢这里提供的答案swagger-codegen client: How to include jackson annotations on models

dw1jzc5e

dw1jzc5e2#

它仍然不为我工作。我使用gradle plugin open-api-generator v6.1.0(我也测试了5.4.0)下面是我的简单swagger:

"components": {
"schemas": {
  "Parent": {
    "type": "object",
    "properties": {
      "vehicle": {
        "$ref": "#/components/schemas/Vehicle"
      }
    }
  },
  "Vehicle": {
    "type": "object",
    "properties": {
      "vehicleType": {
        "type": "string"
      }
    },
    "discriminator": {
      "propertyName": "vehicleType",
      "mapping": {
        "CAR": "#/components/schemas/Car",
        "TRUCK": "#/components/schemas/Truck"
      }
    }
  },
  "Car": {
    "allOf": [
      {
        "$ref": "#/components/schemas/Vehicle"
      },
      {
        "type": "object",
        "properties": {
          "carProperty": {
            "type": "string"
          }
        }
      }
    ]
  },
  "Truck": {
    "allOf": [
      {
        "$ref": "#/components/schemas/Vehicle"
      },
      {
        "type": "object",
        "properties": {
          "truckProperty": {
            "type": "string"
          }
        }
      }
    ]
  }
}

字符串
}
这是我的openApiTaskConfiguration(在build.gradle文件中):

generatorName.set("kotlin")
configFile.set("$rootDir/src/main/resources/swagger/config.json")
inputSpec.set("$rootDir/src/main/resources/swagger/swagger-test.json")
outputDir.set("$generatedSources")
generateModelTests.set(false)
typeMappings.set(mapOf(
    "Date" to "String"))
configOptions.set(mapOf(
    "enumPropertyNaming" to "UPPERCASE",
    "dateLibrary" to "string",
    "interfaceOnly" to "true",
    "serializableModel" to "true",
    "collectionType" to "array",
    "serializationLibrary" to "jackson"
))
globalProperties.set(mapOf("apis" to "false", "models" to "", "modelDocs" to "false"))


}`
Object Vehicle作为接口生成,但在运行时,调用的不是类Car或Truck构造函数,而是接口。抛出的异常
enter image description here

相关问题