OPENAPI/Swagger代码生成器中的AdditionalProperties扩展HashMap:play(jackson)反序列化失败。

6

我的问题有点复杂,我会尽量清楚地解释。为此,我做了一个简单的项目。

我正在使用Swagger代码生成器从Swagger文件生成Java类。 在Swagger文件中,一个定义正在使用additionalProperties。

  MyRequestBody:
    type: object
    properties:
      property1:
        type: string
      property2:
        type: string
    additionalProperties:
      type: object

生成的Java类:
/*
 * Chatbot api
 * Api for chatbot interface.
 *
 * OpenAPI spec version: 1.0
 * 
 *
 * NOTE: This class is auto generated by the swagger code generator program.
 * https://github.com/swagger-api/swagger-codegen.git
 * Do not edit the class manually.
 */


package lu.post.models.api.test;

import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import javax.xml.bind.annotation.*;
/**
 * MyRequestBody
 */
@javax.annotation.Generated(value = "lu.post.codegen.ApiplaylibGenerator")
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@XmlRootElement(name="MyRequestBody")
@XmlAccessorType(XmlAccessType.FIELD)
public class MyRequestBody extends HashMap<String, Object> {
  @JsonProperty("property1")
        @XmlElement(name="property1")
  private String property1 = null;

  @JsonProperty("property2")
        @XmlElement(name="property2")
  private String property2 = null;

  public MyRequestBody property1(String property1) {
    this.property1 = property1;
    return this;
  }

   /**
   * Get property1
   * @return property1
  **/
  @ApiModelProperty(example = "null", value = "")
  public String getProperty1() {
    return property1;
  }

  public void setProperty1(String property1) {
    this.property1 = property1;
  }

  public MyRequestBody property2(String property2) {
    this.property2 = property2;
    return this;
  }

   /**
   * Get property2
   * @return property2
  **/
  @ApiModelProperty(example = "null", value = "")
  public String getProperty2() {
    return property2;
  }

  public void setProperty2(String property2) {
    this.property2 = property2;
  }


  @Override
  public boolean equals(java.lang.Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || !(o instanceof MyRequestBody)) {
      return false;
    }
    MyRequestBody myRequestBody = (MyRequestBody) o;
    return Objects.equals(this.property1, myRequestBody.property1) &&
        Objects.equals(this.property2, myRequestBody.property2) &&
        super.equals(o);
  }

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


  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("class MyRequestBody {\n");
    sb.append("    ").append(toIndentedString(super.toString())).append("\n");
    sb.append("    property1: ").append(toIndentedString(property1)).append("\n");
    sb.append("    property2: ").append(toIndentedString(property2)).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(java.lang.Object o) {
    if (o == null) {
      return "null";
    }
    return o.toString().replace("\n", "\n    ");
  }

}

如您所见,生成的类为additionalProperties扩展了HashMap。

目前为止,没有什么惊人之处。

该类已在play / java项目中使用,使用play库对json和pojo进行序列化/反序列化。

我创建了一个简单的路由和控制器,以执行POST / test并使用以下正文(与swagger定义匹配)

{
    "property1": "p1", 
    "property2": "p2"
}

我的控制器如下:

public Result test() {
    classLogger.debug("==================================");
    classLogger.debug("START test()");
    JsonNode bodyJsonNode = request().body().asJson();
    MyRequestBody myRequestBody = Json.fromJson(bodyJsonNode, MyRequestBody.class);

    classLogger.debug("myRequestBody : ");
    classLogger.debug(myRequestBody.toString());

    classLogger.debug("END test()");
    classLogger.debug("==================================");
    return ok();
}

日志显示问题:

2017-12-16 22:54:15,556[DEBUG][][][][ConversationController]==================================
2017-12-16 22:54:15,556[DEBUG][][][][ConversationController]START test()
2017-12-16 22:54:15,605[DEBUG][][][][ConversationController]myRequestBody :
2017-12-16 22:54:15,605[DEBUG][][][][ConversationController]class MyRequestBody {
    {property2=p2, property1=p1}
    property1: null
    property2: null
}
2017-12-16 22:54:15,605[DEBUG][][][][ConversationController]END test()
2017-12-16 22:54:15,605[DEBUG][][][][ConversationController]==================================

对象字段“property1”和“property2”为空,因为字段名称和值被放在Map的键/值中。

有人知道解决这个问题的最佳方法吗?请注意: - 我无法修改Swagger定义(因为在我的实际项目中,它是由另一个公司提供的)。 - 我希望继续使用Swagger代码生成库。

提前致谢,


你好,有人遇到过这个问题吗? - user3560649
我知道这个问题有点老了,但是我刚刚遇到了由openApi生成器产生的文件相同的问题。 - dfche
我使用ObjectMapper进行如下配置:OBJECT_MAPPER.configOverride(MyClass.class).setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.OBJECT)); - dfche
2个回答

0

我遇到了另一个看似与你的问题相关的问题。 我成功地将属性和地图在我的API定义中分开,这可能对你有帮助,但是生成的代码存在问题,因为它没有导入HashMap.java,尽管使用了它。

模式:

ReportRequest:
  type: object
  properties:
    authentication:
      $ref: '#/components/schemas/SessionAuthentication'
    reportParameters:
      $ref: '#/components/schemas/ReportParameters'
ReportParameters:
  type: object
  properties:
    fromDate:
      type: string
    toDate:
      type: string
  required:
    - fromDate
    - toDate
  additionalProperties: true
  example:  
    fromDate: '2020-04-13'
    toDate: '2022-04-13'

注意,缺少java.util.HashMap的导入和输入错误。

类型不匹配:无法将HashMap<String,Object>转换为ReportParameters

    package io.swagger.model;

import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import io.swagger.model.ReportParameters;
import io.swagger.model.SessionAuthentication;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import javax.validation.constraints.*;

/**
 * ReportRequest
 */
@Validated
@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.SpringCodegen", date = "2022-03-31T11:12:13.135Z[GMT]")

public class ReportRequest   {
          @JsonProperty("authentication")
          private SessionAuthentication authentication = null;
        
          @JsonProperty("reportParameters")
          private ReportParameters reportParameters = new HashMap<String, Object>();

关于additionalProperties的更多信息可以在此处找到


0

不是理想的解决方案,但可以采用以下方法。定义此抽象类:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public abstract class AbstractAdditionalPropertiesDeserializer<T extends HashMap<String, Object>> extends JsonDeserializer<T> {

    private final Class<T> type;

    public AbstractAdditionalPropertiesDeserializer(Class<T> type) {
        this.type = type;
    }

    @Override
    public T deserialize(JsonParser jsonParser, DeserializationContext ctxt)
        throws IOException {
        ObjectCodec oc = jsonParser.getCodec();
        Map<String, Object> node = oc.readValue(jsonParser, Map.class);
        try {
            T obj = type.getConstructor().newInstance();
            obj.putAll(node);
            for( Map.Entry<String, Class<?>> prop : getProperties().entrySet() ) {
                type.getDeclaredMethod("set" + capitalise(prop.getKey()),prop.getValue()).invoke(obj,node.get(prop.getKey()));
            }
            return obj;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new IOException(e);
        }
    }

    private String capitalise(String str) {
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    abstract Map<String,Class<?>> getProperties();
}

然后定义一个实现它的bean,像这样:

public class MyRequestBodyDeserialiser extends AbstractAdditionalPropertiesDeserializer<MyRequestBody> {

    public MyRequestBodyDeserialiser() {
        super(MyRequestBody.class);
    }

    @Override
    Map<String,Class<?>> getProperties() {
        return new HashMap<String,Class<?>>() {
            {
                put( "property1" ,String.class);
            }
        };
    }
}

将其注入到对象映射器配置中,使您能够反序列化JSON有效负载,并设置所需的属性


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接