簡體   English   中英

springdoc-openapi 如何在不更改 toString 的情況下使用 @JsonValue 枚舉格式?

[英]How can have springdoc-openapi use the @JsonValue enum format without changing toString?

我有一個 Spring Boot 應用程序,它使用springdoc-openapi為我的控制器生成 Swagger API 文檔。 JSON 請求/響應中使用的枚舉之一具有與其value / toString()不同的 JSON 表示。 這是使用 Jackson @JsonValue注釋實現的:

public enum Suit {
    HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");

    @JsonValue
    private final String jsonValue;

    Suit(String jsonValue) { this.jsonValue = jsonValue; }
}

但是,在列出枚舉值時,生成的 Swagger API 文檔使用枚舉值(特別是toString()的值)而不是 JSON 表示(每個@JsonValue ):

{
  "openapi": "3.0.1",
  "info": { "title": "OpenAPI definition", "version": "v0" },
  "servers": [
    { "url": "http://localhost:8080", "description": "Generated server url" }
  ],
  "paths": { ... },
  "components": {
    "schemas": {
      "PlayingCard": {
        "type": "object",
        "properties": {
          "suit": {
            "type": "string",
            "enum": [ "Hearts", "Diamonds", "Clubs", "Spades" ]
          },
          "value": { "type": "integer", "format": "int32" }
        }
      }
    }
  }
}

springdoc-openapi 項目中有一個已關閉的問題#1101 ,它請求允許@JsonValue影響枚舉序列化。 但是,該問題已關閉,因為沒有提交任何 PR。

如何獲取枚舉列表以匹配 REST 端點接受/返回的實際 JSON 類型,而不是toString()值?

我解決這個問題的第一個想法是使用來自Swagger Core@Schema(allowableValues = {...}]注釋。但是,無論是由於錯誤還是設計,這都會添加到值列表中,而不是替換它:

@Schema(allowableValues = {"Hearts", "Diamonds", "Clubs", "Spades"})
public enum Suit {
    HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");
    // ...
}
"suit": {
  "type": "string",
  "enum": [
    "HEARTS", 
    "DIAMONDS",
    "CLUBS",
    "SPADES",
    "Hearts",
    "Diamonds",
    "Clubs",
    "Spades"
  ]
}

可重現的例子

plugins {
    id 'org.springframework.boot' version '2.5.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'io.swagger.core.v3:swagger-annotations:2.1.10'
    implementation 'org.springdoc:springdoc-openapi-ui:1.5.10'
    implementation 'org.springframework.boot:spring-boot-starter-web'
}
package com.example.springdoc;

import com.fasterxml.jackson.annotation.JsonValue;

public class PlayingCard {
    private Suit suit;
    private Integer value;

    public Suit getSuit() { return suit; }
    public void setSuit(Suit suit) { this.suit = suit; }
    public Integer getValue() { return value; }
    public void setValue(Integer value) { this.value = value; }

    public enum Suit {
        HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");

        @JsonValue
        private final String jsonValue;

        Suit(String jsonValue) { this.jsonValue = jsonValue; }
    }
}
package com.example.springdoc;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/playingCard")
public class PlayingCardController {
    @PostMapping
    public PlayingCard echo(@RequestBody PlayingCard card) {
        return card;
    }
}

招搖網址: http://localhost:8080/v3/api-docs

可以創建一個PropertyCustomizer Spring bean 來自定義屬性。 這可以針對特定的枚舉類型執行,也可以針對所有枚舉全局執行。

具有顯式列表的特定於類型的定制器

以下定制器將顯式設置特定枚舉類型的枚舉值:

import com.fasterxml.jackson.databind.JavaType;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.springdoc.core.customizers.PropertyCustomizer;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class SuitPropertyCustomizer implements PropertyCustomizer {
    @Override
    public Schema customize(Schema property, AnnotatedType type) {
        if (property instanceof StringSchema && isSuit(type)) {
            property.setEnum(List.of("Hearts", "Diamonds", "Clubs", "Spades"));
        }

        return property;
    }

    private boolean isSuit(AnnotatedType type) {
        return type.getType() instanceof JavaType && ((JavaType) type.getType()).isTypeOrSubTypeOf(Suit.class);
    }
}

使用@JsonValue全局枚舉定制器

以下定制器將對所有枚舉類型使用 Jackson 字符串表示,這意味着將在適用的情況下使用@JsonValue注釋。

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.springdoc.core.customizers.PropertyCustomizer;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.stream.Collectors;

@Component
public class EnumValuePropertyCustomizer implements PropertyCustomizer {
    @Override
    public Schema customize(Schema property, AnnotatedType type) {
        if (property instanceof StringSchema && isEnumType(type)) {
            ObjectMapper objectMapper = Json.mapper();

            property.setEnum(Arrays.stream(((JavaType) type.getType()).getRawClass().getEnumConstants())
                    .map(e -> objectMapper.convertValue(e, String.class))
                    .collect(Collectors.toList()));
        }
        return property;
    }

    private boolean isEnumType(AnnotatedType type) {
        return type.getType() instanceof JavaType && ((JavaType) type.getType()).isEnumType();
    }
}

由於Swagger Core 中的錯誤#3998@JsonValue在公共方法上可以正確處理,但在字段上則不能。 因此,添加公共訪問器方法將按需要工作:

public enum Suit {
    HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");

    private final String jsonValue;

    Suit(String jsonValue) { this.jsonValue = jsonValue; }

    @JsonValue
    public String getJsonValue() {
        return jsonValue;
    }
}

一種解決方案是用@JsonProperty替換@JsonValue實現:

public enum Suit {
    @JsonProperty("Hearts") HEARTS,
    @JsonProperty("Diamonds") DIAMONDS,
    @JsonProperty("Clubs") CLUBS,
    @JsonProperty("Spades") SPADES;
}

請注意,如果以編程方式需要該值,這確實會導致一些重復,因為它需要在@JsonProperty以及枚舉上的值中指定。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM