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

> Learn how CoApi manages request and response body conversion for JSON, XML, and custom formats. Discover its integration with Spring WebFlux WebClient for seamless data handling.

- Repository: [Ahoo Wang/coapi](https://github.com/ahoo-wang/coapi)
- Tags: deep-dive
- Published: 2026-02-23

---

**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`](https://github.com/ahoo-wang/coapi/blob/main/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`](https://github.com/ahoo-wang/coapi/blob/main/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

```kotlin
@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

```kotlin
@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

```kotlin
@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`](https://github.com/ahoo-wang/coapi/blob/main/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`](https://github.com/ahoo-wang/coapi/blob/main/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.