首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何让springdoc-openapi在不更改toString的情况下使用@JsonValue枚举格式?

如何让springdoc-openapi在不更改toString的情况下使用@JsonValue枚举格式?
EN

Stack Overflow用户
提问于 2021-08-11 18:10:51
回答 3查看 1.6K关注 0票数 2

我有一个Spring Boot应用程序,它使用springdoc-openapi为我的控制器生成Swagger API文档。JSON请求/响应中使用的枚举之一具有与其value/toString()不同的JSON表示。这是通过使用Jackson @JsonValue注释实现的:

代码语言:javascript
运行
复制
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):

代码语言:javascript
运行
复制
{
  "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影响枚举序列化。然而,这个问题是closed,因为没有为它提交PR。

如何让枚举列表匹配REST端点接受/返回的实际toString()类型,而不是JSON值?

我解决这个问题的第一个想法是使用Swagger Core中的`@Schema(allowableValues = {...}`](https://javadoc.io/static/io.swagger.core.v3/swagger-annotations/2.1.10/io/swagger/v3/oas/annotations/media/Schema.html)注释。但是,无论是由于错误还是出于设计原因,这都会添加到值列表中,而不是替换它:

代码语言:javascript
运行
复制
@Schema(allowableValues = {"Hearts", "Diamonds", "Clubs", "Spades"})
public enum Suit {
    HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");
    // ...
}
代码语言:javascript
运行
复制
"suit": {
  "type": "string",
  "enum": [
    "HEARTS", 
    "DIAMONDS",
    "CLUBS",
    "SPADES",
    "Hearts",
    "Diamonds",
    "Clubs",
    "Spades"
  ]
}

可重现的例子

代码语言:javascript
运行
复制
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'
}
代码语言:javascript
运行
复制
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; }
    }
}
代码语言:javascript
运行
复制
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;
    }
}

Swagger网址:http://localhost:8080/v3/api-docs

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-08-17 22:17:52

由于Swagger Core中的错误#3998,在公共方法上可以正确处理@JsonValue,但在字段上则无法正确处理。因此,添加一个公共访问器方法将按预期工作:

代码语言:javascript
运行
复制
public enum Suit {
    HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");

    private final String jsonValue;

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

    @JsonValue
    private String getJsonValue() {
        return jsonValue;
    }
}
票数 3
EN

Stack Overflow用户

发布于 2021-08-11 20:00:26

可以创建一个PropertyCustomizer Spring bean来自定义该属性。这可以针对特定枚举类型执行,也可以针对所有枚举全局执行。

具有显式列表的特定类型的定制器

以下定制器将显式设置特定枚举类型的枚举值:

代码语言:javascript
运行
复制
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注释。

代码语言:javascript
运行
复制
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();
    }
}
票数 1
EN

Stack Overflow用户

发布于 2021-08-11 18:49:37

一种解决方案是用@JsonProperty替换@JsonValue实现

代码语言:javascript
运行
复制
public enum Suit {
    @JsonProperty("Hearts") HEARTS,
    @JsonProperty("Diamonds") DIAMONDS,
    @JsonProperty("Clubs") CLUBS,
    @JsonProperty("Spades") SPADES;
}

请注意,如果以编程方式需要该值,这确实会导致一些重复,因为它需要在@JsonProperty中指定,也需要在枚举上指定一个值。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68747036

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档