How CoApi Handles Request and Response Body Conversion (JSON, XML, and Custom Formats)

CoApi delegates request and response body conversion to Spring WebFlux's WebClient, automatically using Jackson-based encoders and decoders for JSON and XML based on Content-Type and Accept headers.

CoApi is an annotation-driven HTTP client library for Spring Boot that simplifies API consumption through interface proxies. Understanding how CoApi handles request and response body conversion is essential for integrating with REST services that use JSON, XML, or custom media formats. This article examines the source code of ahoo-wang/coapi to reveal the conversion mechanisms that operate behind the @CoApi annotation.

Spring WebFlux Codec Architecture

CoApi does not implement its own serialization logic. Instead, it relies on Spring WebFlux's WebClient and the standard HttpMessageConverter chain that Spring registers automatically.

When you annotate an interface with @CoApi, Spring creates a dynamic proxy that routes HTTP requests through a WebClient.Builder instance. This builder is pre-configured with default codecs from the CodecConfigurer. The conversion pipeline automatically selects the appropriate encoder for request bodies and decoder for response payloads based on the declared media types.

Default Jackson-Based Converters

According to the source code analysis, CoApi inherits Spring's default codec selection:

  • JSON: Uses Jackson2JsonEncoder and Jackson2JsonDecoder backed by the application's ObjectMapper
  • XML: Uses Jackson2XmlEncoder and Jackson2XmlDecoder when the Jackson XML module is present on the classpath
  • Other types: String, ByteArray, and other built-in codecs handle text/plain or application/octet-stream

The @CoApi annotation in api/src/main/kotlin/me/ahoo/coapi/api/CoApi.kt serves as the entry point that triggers Spring's auto-configuration to supply the WebClient.Builder bean, which carries these converters.

Automatic Content Negotiation for JSON and XML

CoApi handles content negotiation transparently by examining the Accept and Content-Type headers of each request.

For JSON endpoints, the proxy automatically serializes Kotlin data classes into JSON request bodies using Jackson2JsonEncoder. When receiving responses, Jackson2JsonDecoder maps the JSON payload to the declared return type.

For XML endpoints, when you specify accept = ["application/xml"] or the API returns XML content, Spring automatically selects Jackson2XmlDecoder to unmarshall the response into your target class. This requires the jackson-dataformat-xml dependency.

Customizing Body Conversion with WebClient.BuilderCustomizer

To add custom converters or modify the default ObjectMapper, CoApi supports Spring's WebClient.BuilderCustomizer interface.

In example/example-consumer-server/src/main/kotlin/me/ahoo/coapi/example/consumer/ConsumerWebClientBuilderCustomizer.kt, the example demonstrates how to access the WebClient.Builder to customize the underlying HttpClient and codec configuration. You can register custom Encoder or Decoder implementations by providing a customizer bean that modifies the CodecConfigurer.

Practical Code Examples

Consuming JSON APIs

@CoApi(baseUrl = "https://api.example.com")
interface UserClient {
    @GetExchange("/users/{id}")
    suspend fun getUser(@PathVariable id: String): UserDto
    
    @PostExchange("/users")
    suspend fun createUser(@RequestBody user: UserCreateRequest): UserDto
}

The UserDto and UserCreateRequest classes are automatically serialized and deserialized using Jackson without explicit mapper configuration.

Handling XML Responses

@CoApi(baseUrl = "https://api.example.com")
interface XmlCatalogClient {
    @GetExchange(value = ["/catalog/{id}"], accept = ["application/xml"])
    suspend fun getCatalogItem(@PathVariable id: String): CatalogItem
}

Spring detects the XML accept header and uses Jackson2XmlDecoder to convert the XML response into the CatalogItem data class.

Registering a Custom Codec

@Component
class ProtobufWebClientCustomizer : WebClient.BuilderCustomizer {
    override fun customize(builder: WebClient.Builder) {
        builder.codecs { configurer ->
            configurer.customCodecs().register(ProtobufDecoder())
            configurer.customCodecs().register(ProtobufEncoder())
        }
    }
}

This customizer adds Protocol Buffers support to all @CoApi clients by registering custom codecs with the WebClient.Builder.

Summary

  • CoApi leverages Spring WebFlux's WebClient rather than implementing custom serialization logic
  • JSON conversion uses Jackson2JsonEncoder/Jackson2JsonDecoder automatically when Jackson is on the classpath
  • XML conversion uses Jackson2XmlEncoder/Jackson2XmlDecoder when the appropriate content-type headers are present and Jackson XML is available
  • The @CoApi annotation in api/src/main/kotlin/me/ahoo/coapi/api/CoApi.kt triggers auto-configuration that wires the codec chain
  • Custom converters can be added via WebClient.BuilderCustomizer beans, as demonstrated in ConsumerWebClientBuilderCustomizer.kt

Frequently Asked Questions

Does CoApi support JSON serialization by default?

Yes. CoApi automatically supports JSON through Spring's default HttpMessageConverters. When Jackson is on the classpath, the WebClient uses Jackson2JsonEncoder and Jackson2JsonDecoder to handle request and response bodies without requiring explicit configuration in your CoApi interfaces.

How do I handle XML responses in CoApi?

Ensure you have jackson-dataformat-xml in your dependencies, then specify accept = ["application/xml"] in your @GetExchange or other HTTP method annotations. Spring automatically selects the XML decoder based on content-type negotiation, using Jackson2XmlDecoder to unmarshall responses into your declared return types.

Can I use a custom ObjectMapper with CoApi?

Yes. While CoApi uses the default Spring ObjectMapper, you can customize it by providing a WebClient.BuilderCustomizer bean that modifies the codec configuration and supplies a custom ObjectMapper to the Jackson encoders and decoders. Alternatively, define a custom ObjectMapper bean in your Spring context, which Spring will use when creating the default Jackson codecs.

How do I add support for Protocol Buffers or other binary formats?

Register custom Encoder and Decoder implementations through a WebClient.BuilderCustomizer component. By calling configurer.customCodecs().register() within the customizer, you can add support for Protocol Buffers, Avro, or any other format to your CoApi clients.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →