springboot集成swagger gson序列化异常

1,373 阅读2分钟
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.7.0</version>
            </dependency>

            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.7.0</version>
            </dependency>   

增加开启

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author 小蓝子
 * @date 2020/11/2
 */
@EnableSwagger2
@Configuration
public class SwaggerConfiguration {
}

添加后重启访问127.0.0.1:8080

发现页面无法打开

http://127.0.0.1/app-swagger/v2/api-docs

返回的数据结构是如下有很多斜杠这是gson序列化导致前端ui无法解析

"value": "{\"swagger\":\"2.0\",\"info\":{\"description\":\"this is restful api document\",\"version\":\"1.0.0\",\"title\":\"MyApp API文档\",\"contact\":{\"name\":\"xxx\",\"url\":\"http://xxxx.com\",\"email\":\"xxx@xxx.com\"}},\"host\":\"localhost:8080\",\"basePath\":\"/\",\"tags\":[{\"name\":\"login-controller\",\"description\":\"Login Controller\"},{\"name\":\"user-controller\",\"description\":\"User Controller\"}],\"paths\"

先说解决方法吧

新增以下的类

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import springfox.documentation.spring.web.json.Json;

import java.lang.reflect.Type;

public class SwaggerGsonAdapter implements JsonSerializer<Json> {
    @Override
    public JsonElement serialize(Json json, Type type, JsonSerializationContext context) {
        final JsonParser parser = new JsonParser();
        return parser.parse(json.value());
    }
}

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.netease.godlike.domain.gson.GsonView;
import com.netease.godlike.infrastructure.gson.GsonIgnoreStrategy;
import com.netease.godlike.infrastructure.gson.JsonFieldNamingPolicy;
import com.netease.godlike.swagger.adapter.SwaggerGsonAdapter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import springfox.documentation.spring.web.json.Json;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 
 * @date 2020/3/11
 **/

public class SwaggerGsonMessageConverter implements HttpMessageConverter<Json> {
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
    private Gson gson = new GsonBuilder()
            .registerTypeHierarchyAdapter(Json.class, new SwaggerGsonAdapter())
            .create();

    @Override
    public boolean canRead(Class<?> aClass, MediaType mediaType) {

        return false;
    }

    @Override
    public boolean canWrite(Class<?> aClass, MediaType mediaType) {
        return Json.class.getName().equals(aClass.getName());
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return Arrays.asList(MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
    }

    @Override
    public Json read(Class<? extends Json> aClass, HttpInputMessage httpInputMessage) throws HttpMessageNotReadableException {
        return null;
    }

    @Override
    public void write(Json json, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
        Charset charset = this.getCharset(httpOutputMessage.getHeaders());
        OutputStreamWriter writer = new OutputStreamWriter(httpOutputMessage.getBody(), charset);
        try {
            this.gson.toJson(json, writer);
            writer.close();
        } catch (JsonIOException var7) {
            throw new HttpMessageNotWritableException("Could not write JSON: " + var7.getMessage(), var7);
        }
    }

    private Charset getCharset(HttpHeaders headers) {
        return headers != null && headers.getContentType() != null && headers.getContentType().getCharset() != null ? headers.getContentType().getCharset() : DEFAULT_CHARSET;
    }
}

import com.netease.godlike.swagger.converter.SwaggerGsonMessageConverter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration;

/**
 * @author 小蓝子
 * @date 2020/11/2
 */
@Configuration
@ConditionalOnBean(Swagger2DocumentationConfiguration.class)
public class SwaggerCommonConfiguration {

    @Bean
    public SwaggerGsonMessageConverter swaggerGsonMessageConverter() {
        return new SwaggerGsonMessageConverter();
    }

}

@ConditionalOnBean(Swagger2DocumentationConfiguration.class)
这个最好加下限制,如果不开启swagger就不要注入这个bean

完了重启就好了

查找问题:

查找Swagger2Controller 可以看到文档接口返回的对象是Json

 @RequestMapping(
        value = {"/v2/api-docs"},
        method = {RequestMethod.GET},
        produces = {"application/json", "application/hal+json"}
    )
    @PropertySourcedMapping(
        value = "${springfox.documentation.swagger.v2.path}",
        propertyKey = "springfox.documentation.swagger.v2.path"
    )
    @ResponseBody
    public ResponseEntity<Json> getDocumentation(@RequestParam(value = "group",required = false) String swaggerGroup, HttpServletRequest servletRequest) {
        String groupName = (String)Optional.fromNullable(swaggerGroup).or("default");
        Documentation documentation = this.documentationCache.documentationByGroup(groupName);
        if (documentation == null) {
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        } else {
            Swagger swagger = this.mapper.mapDocumentation(documentation);
            UriComponents uriComponents = HostNameProvider.componentsFrom(servletRequest, swagger.getBasePath());
            swagger.basePath(Strings.isNullOrEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath());
            if (Strings.isNullOrEmpty(swagger.getHost())) {
                swagger.host(this.hostName(uriComponents));
            }

            return new ResponseEntity(this.jsonSerializer.toJson(swagger), HttpStatus.OK);
        }
    }

gson序列化采用的是com.google.gson.JsonSerializer

发现子类没有对应的Adapter

增加对应的Adapter实现类并把规则添加到对应的gson序列化实例当中

 private Gson gson = new GsonBuilder()
            .registerTypeHierarchyAdapter(Json.class, new SwaggerGsonAdapter())
            .create();

然后实现spring的HttpMessageConverter类

主要是

write的方法,例子可以通过f4看HttpMessageConverter的子类实现抄过来就行了,替换对应的

并注入到spring容器中