对生成的子类进行反序列化会导致父对象



版本: Swagger-codegen (v3(: 3.0.11 |杰克逊: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"

这将生成两个类:

通知.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);
}
}

和电子邮件通知:

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 的最佳解决方案。

原来缺少@JsonSubType注释。由于ObjectMapper不与 swagger 通信,因此它永远无法知道该类应该映射到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在反序列化时理解的映射。

特别感谢提供的答案 这里是 swagger-codegen 客户端:如何在模型上包含杰克逊注释

它仍然对我不起作用。我使用 gradle 插件开放 api-generator v6.1.0(我也用 5.4.0 测试过( 这是我的简单招摇:

"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"))

}'

对象车辆作为接口生成,但在运行时,这从来不是调用的类 Car 或 Truck 构造函数,而是接口。抛出异常

在此处输入图像描述

最新更新