1

Project setup

I have a Kotlin Spring Boot 2.0 project that exposes a @RestController API that returns MongoDB models. For example, this model and controller:

@RestController
@RequestMapping("/api/accounts")
class AccountsController() {
    @GetMapping
    fun list(): List<Account> {
        return listOf(Account(ObjectId(), "Account 1"), Account(ObjectId(), "Account 2"), Account(ObjectId(), "Account 3"))
    }
}

@Document
data class Account(
        @Id val id: ObjectId? = null,
        val name: String
)

These models have ObjectId identifiers, but in the API I want them to be treated as plain String (i.e. instead of a complex JSON, the default behaviour).

To achieve this, I created these components to configure Spring Boot parameter binding and JSON parsing:

@JsonComponent
class ObjectIdJsonSerializer : JsonSerializer<ObjectId>() {
    override fun serialize(value: ObjectId?, gen: JsonGenerator?, serializers: SerializerProvider?) {
        if (value == null || gen == null) return

        gen.writeString(value.toHexString())
    }
}

@JsonComponent
class ObjectIdJsonDeserializer : JsonDeserializer<ObjectId>() {
    override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): ObjectId? {
        if (p == null) return null

        val text = p.getCodec().readTree<TextNode>(p).textValue()
        return ObjectId(text)

    }
}

@Component
class StringToObjectIdConverter : Converter<String, ObjectId> {
    override fun convert(source: String): ObjectId? {
        return ObjectId(source)
    }
}

So far this works as intended, calls to the API return this JSON:

[
  {
    "id": "5da454f4307b0a8b30838839",
    "name": "Account 1"
  },
  {
    "id": "5da454f4307b0a8b3083883a",
    "name": "Account 2"
  },
  {
    "id": "5da454f4307b0a8b3083883b",
    "name": "Account 3"
  }
]

Issue

The problem comes when integrating Swagger into the project, the documentation shows that calling this method returns a complex JSON instead of a plain String as the id property:

enter image description here

Adding @ApiModelProperty(dataType = "string") to the id field made no difference, and I can't find a way to solve it without changing all the id fields in the project to String. Any help would be appreciated.

redent84
  • 18,901
  • 4
  • 62
  • 85
  • Maybe my solution to this problem in C # will be useful for you: https://stackoverflow.com/a/59800065/908936 – razon Jan 18 '20 at 13:15

2 Answers2

1

I couldn't get @ApiModelProperty(dataType = "") to work, but I found a more convenient way configuring a direct substitute in the Swagger configuration using directModelSubstitute method of the Docket instance in this response.

@Configuration
@EnableSwagger2
class SwaggerConfig() {
    @Bean
    fun api(): Docket {
        return Docket(DocumentationType.SWAGGER_2)
                .directModelSubstitute(ObjectId::class.java, String::class.java)
    }
}

Java equivalent:

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .directModelSubstitute(ObjectId.class, String.class);
}
redent84
  • 18,901
  • 4
  • 62
  • 85
1

For OpenApi (Swagger 3.0) and SpringDoc the following global configuration could be used.

static {
     SpringDocUtils.getConfig().replaceWithSchema(ObjectId.class, new StringSchema());
}
vzhemevko
  • 815
  • 8
  • 25