Swagger-ui shows null for List<MonetartyAmount>

Refresh

December 2018

Views

159 time

5

I am using swagger on my springboot project. The problem appears for endpoint which returns the entity with list of MonetaryAmount i.e. (List<MonetaryAmount> rates)

Swagger-ui shows incorrect data for such endpoints as:

{
  "rates": [
    null
  ]
}

However I expect to see each MonetaryAmount value as pair of Double and String. Smth like this:

{
  "rates": [
   {"currency":"EUR", "rate": 12.23}
  ]
}

I tried to use directModelSubstitute but it seems it is not working for List.

Here is my model :

public class CurrencyRatesResponse implements Serializable {

private List<MonetaryAmount> rates;

public CurrencyRatesResponse() {
}

public CurrencyRatesResponse(List<MonetaryAmount> rates) {
    this.rates = rates;
}

public List<MonetaryAmount> getRates() {
    return rates;
}

public void setRates(List<MonetaryAmount> rates) {
    this.rates = rates;
}
}

And here is an example of my endpoint

@RequestMapping(path = "/public/rates", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public CurrencyRatesResponse getRates() {...}

Generated swagger JSON:

 "/public/rates":{
         "get":{
            "tags":[
               "rate-endpoint"
            ],
            "summary":"getRates",
            "operationId":"getRatesUsingGET",
            "produces":[
               "application/json;charset=UTF-8"
            ],
            "parameters":[
               {
                 ....
               }
            ],
            "responses":{
               "200":{
                  "description":"OK",
                  "schema":{
                     "$ref":"#/definitions/CurrencyRatesResponse"
                  }
               },
               "401":{
                  "description":"Unauthorized"
               },
               "403":{
                  "description":"Forbidden"
               },
               "404":{
                  "description":"Not Found"
               }
            }
         }
      }
    }  ....

   "CurrencyRatesResponse":{
         "type":"object",
         "properties":{
            "rates":{
               "type":"array",
               "items":{
                  "$ref":"#/definitions/MonetaryAmount"
               }
            }
         },
         "title":"CurrencyRatesResponse"
      },

1 answers

4

Есть несколько шагов для достижения этой цели.

Прежде всего, нам нужно собственно ObjectMapper так MonetaryAmountполучает отображается на соответствующий объект с двумя полями currencyи rate. Для достижения этой цели нам нужно добавить пользовательский модуль для ObjectMapper в springfox в.

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.zalando.jackson.datatype.money.MoneyModule;
import springfox.documentation.schema.configuration.ObjectMapperConfigured;

@Component
public class SwaggerJacksonModule implements ApplicationListener<ObjectMapperConfigured> {

    @Override
    public void onApplicationEvent(ObjectMapperConfigured event) {
        event.getObjectMapper().registerModule(new MoneyModule());
    }
}

Объяснение: Мы добавляем слушатель в springfox в ObjectMapperConfiguredслучай и когда событие срабатывает , мы просто получить картограф и зарегистрировать наш модуль. Я использую Zalando в библиотеку Джексона Datatype деньги , так что я не пишу сам.

Следующий шаг должен был бы работать на фактическом представлении Swagger. Давайте создадим наш собственный объект представление , MonetaryWrapperкоторое мы будем использовать его для замены MonetaryAmount.

public class MonetaryWrapper {

    private BigDecimal amount;

    private String currency;

  // getters and setters  (getters are important)
}

Для того , чтобы получить Одиночные представления объекта права этого достаточно , чтобы добавить .directModelSubstitute(MonetaryAmount.class, MonetaryWrapper.class)к Docketобъекту.

НО это не так просто для коллекции. например , список в этом случае. Нам нужно добавить Специальный AlternateTypeRuleобъект в Docketобъект для того , чтобы списки показывают правильно.

Подробнее на эту тему в springfox в документации

Что-то вроде этого:

TypeResolver resolver = new TypeResolver();
AlternateTypeRule monetaryAmountListRule =
        newRule(resolver.resolve(List.class,  MonetaryAmount.class),
                resolver.resolve(List.class, MonetaryWrapper.class))

И , наконец , наша Docketфасоль будет выглядеть примерно так:

@Bean
public Docket productApi() {
    TypeResolver resolver = new TypeResolver();
    AlternateTypeRule monetaryAmountListRule =
            newRule(resolver.resolve(List.class,  MonetaryAmount.class),
                    resolver.resolve(List.class, MonetaryWrapper.class));


    return new Docket(DocumentationType.SWAGGER_2)
            .alternateTypeRules(monetaryAmountListRule)
            .directModelSubstitute(MonetaryAmount.class, MonetaryWrapper.class)
            .select().apis(RequestHandlerSelectors.basePackage("com.swagger.test"))
            .paths(PathSelectors.regex("/public.*")).build();
}

И некоторые скриншоты здесь:

Демонстрационный при выполнении запроса:

DEMO конечного результат

Демонстрационный только для описания объекта :

Демо результат представления