# Payload conversion

> For the complete documentation index, see [llms.txt](https://docs.temporal.io/llms.txt).
> Any documentation page is available as raw Markdown by appending `.md` to its URL.
> For related questions, query https://docs.temporal.io/api/retrieve?q=<question>&jtb=<job-to-be-done>&src=md-page&agent=<your-name> (no auth; returns JSON `{content, source_url, page_url}`, with `source_url` pointing to Markdown for docs pages). Don't include personal data or credentials.
> If this page is wrong or missing something you needed, or Temporal behaves differently from what it says, report it with https://docs.temporal.io/api/feedback?title=<summary>&message=<what-happened>&category=documentation&page_url=/develop/java/best-practices/data-handling/data-conversion&src=md-page&agent=<your-name> (use `category=product` when the product, not the page, is wrong). Report only a specific problem you hit, send it once, and don't include personal data or credentials.

> Customize how Temporal serializes application objects using Payload Converters in the Java SDK.

Payload Converters serialize your application objects into a `Payload` and deserialize them back.
A `Payload` is a binary form with metadata that Temporal uses to transport data.

By default, Temporal uses a Payload Converter that handles `null`, byte arrays, protobuf messages, and anything JSON-serializable.
You only need a custom Payload Converter when your application uses types that aren't natively supported.

## Default supported types

The default Data Converter supports converting multiple types including:

- `null`
- Byte arrays
- Protobuf JSON: if a value is an instance of a Protobuf message, it is encoded with proto3 JSON
- [Jackson JSON](https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/jackson.html)
- Anything that can be converted to JSON

## Using custom Payload conversion 

Temporal SDKs provide a [Payload Converter](/payload-converter) that can be customized to convert a custom data type to [Payload](/dataconversion#payload) and back.

Implementing custom Payload conversion is optional.
It is needed only if the [default Data Converter](/default-custom-data-converters#default-data-converter) does not support your custom values.

To support custom Payload conversion, create a [custom Payload Converter](/payload-converter#composite-data-converters) and configure the Data Converter to use it in your Client options.

The order in which your encoding Payload Converters are applied depend on the order given to the Data Converter.
You can set multiple encoding Payload Converters to run your conversions.
When the Data Converter receives a value for conversion, it passes through each Payload Converter in sequence until the converter that handles the data type does the conversion.

Payload Converters can be customized independently of a Payload Codec.
Temporal's Converter architecture looks like this:

![Temporal converter architecture](https://docs.temporal.io/img/info/converter-architecture.png)

Create a custom implementation of a [PayloadConverter](https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/common/converter/PayloadConverter.html) interface and use the `withPayloadConverterOverrides` method to implement the custom object conversion with `DefaultDataConverter`.

`PayloadConverter` serializes and deserializes method parameters that need to be sent over the wire.
You can create a custom implementation of `PayloadConverter` for custom formats.

`withPayloadConverterOverrides` replaces the default converter that has the same encoding type, in the same position.
A converter with a new encoding type is added after the default converters instead.
The default Jackson converter accepts almost any object, so a converter with a new encoding type only receives values
that Jackson can't serialize.
To change how a JSON value is serialized, use the `json/plain` encoding type and delegate every value you don't
handle to `JacksonJsonPayloadConverter`.

The following example writes a `Money` value as a JSON string such as `"12.34 USD"` and reads it back.
`Money` has no default constructor, so the default Jackson converter can serialize it but can't deserialize it:

<!--SNIPSTART java-data-conversion-custom-payload-converter-->
[java-sandbox/best-practices/data-conversion/src/main/java/io/temporal/docs/bestpractices/dataconversion/MoneyPayloadConverter.java](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/java-sandbox/best-practices/data-conversion/src/main/java/io/temporal/docs/bestpractices/dataconversion/MoneyPayloadConverter.java)
```java
/** Payload Converter that writes Money as a JSON string such as "12.34 USD". */
public class MoneyPayloadConverter implements PayloadConverter {
    // This converter replaces the default "json/plain" converter, so it delegates every
    // other value to Jackson.
    private final JacksonJsonPayloadConverter jackson = new JacksonJsonPayloadConverter();

    @Override
    public String getEncodingType() {
        return "json/plain"; // The encoding type determines which default conversion behavior to override.
    }

    @Override
    public Optional<Payload> toData(Object value) throws DataConverterException {
        if (!(value instanceof Money money)) {
            return jackson.toData(value);
        }
        String json = "\"" + money + "\"";
        return Optional.of(
            Payload.newBuilder()
                .putMetadata(EncodingKeys.METADATA_ENCODING_KEY, ByteString.copyFromUtf8(getEncodingType()))
                .setData(ByteString.copyFrom(json, StandardCharsets.UTF_8))
                .build());
    }

    @Override
    public <T> T fromData(Payload content, Class<T> valueClass, Type valueType)
        throws DataConverterException {
        if (valueClass != Money.class) {
            return jackson.fromData(content, valueClass, valueType);
        }
        String text = jackson.fromData(content, String.class, String.class);
        return valueClass.cast(Money.parse(text));
    }
}
```
<!--SNIPEND-->

You can also use [specific implementation classes](https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/common/converter/package-summary.html) provided in the Java SDK.

For example, to create a custom `JacksonJsonPayloadConverter`, use the following.
Start from `JacksonJsonPayloadConverter.newDefaultObjectMapper()` rather than `new ObjectMapper()`: the default mapper
registers the modules for Java time types and `Optional`, and a plain `ObjectMapper` fails to serialize an `Instant`.

<!--SNIPSTART java-data-conversion-custom-jackson {"enable_code_dedenting": true}-->
[java-sandbox/best-practices/data-conversion/src/main/java/io/temporal/docs/bestpractices/dataconversion/Converters.java](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/java-sandbox/best-practices/data-conversion/src/main/java/io/temporal/docs/bestpractices/dataconversion/Converters.java)
```java
public static JacksonJsonPayloadConverter yourCustomJacksonJsonPayloadConverter() {
    // Start from the SDK's default ObjectMapper so Java time types, Optional, and
    // field visibility keep working, then add your changes.
    ObjectMapper objectMapper = JacksonJsonPayloadConverter.newDefaultObjectMapper();
    objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
    return new JacksonJsonPayloadConverter(objectMapper);
}
```
<!--SNIPEND-->

To set your custom Payload Converter, use it with [withPayloadConverterOverrides](https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/common/converter/DefaultDataConverter.html#withPayloadConverterOverrides(io.temporal.common.converter.PayloadConverter...)) with a new instance of `DefaultDataConverter` in your `WorkflowClient` options that you use in your Worker process and to start your Workflow Executions.

The following example sets the `MoneyPayloadConverter` Payload Converter.

<!--SNIPSTART java-data-conversion-set-converter {"enable_code_dedenting": true}-->
[java-sandbox/best-practices/data-conversion/src/main/java/io/temporal/docs/bestpractices/dataconversion/Converters.java](https://github.com/temporalio/documentation-sdk-code-examples/blob/main/java-sandbox/best-practices/data-conversion/src/main/java/io/temporal/docs/bestpractices/dataconversion/Converters.java)
```java
DefaultDataConverter ddc =
    DefaultDataConverter.newDefaultInstance()
        .withPayloadConverterOverrides(new MoneyPayloadConverter());

WorkflowClientOptions workflowClientOptions =
    WorkflowClientOptions.newBuilder().setDataConverter(ddc).build();
```
<!--SNIPEND-->
