# Transformers

In this guide we will see how we can use transformers to map fields. Field transformers are a way to transform a field from one type to another when mapping it to a destination class. For example, you might want to map a field from a `String` to a `List<String>` where the source field is comma delimited. In our example, we will explore this use case, as well as an implicit (default) transformation from `Date` to `Long` (milliseconds).

## Classes

Like before, we start by defining our source and destination classes:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
data class SimpleEntity(
    val creationDate: Date,
    val commaDelimitedString: String, 
)
```

{% endtab %}

{% tab title="Java" %}

```java
public class SimpleEntity {
    private Date creationDate;
    private String commaDelimitedString;

    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    public String getCommaDelimitedString() {
        return commaDelimitedString;
    }

    public void setCommaDelimitedString(String commaDelimitedString) {
        this.commaDelimitedString = commaDelimitedString;
    }
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
data class SimpleEntityDisplay(
    val creationDate: Long = 0,
    val stringList: List<String> = emptyList()
)
```

{% endtab %}

{% tab title="Java" %}

```java
public class SimpleEntityDisplay {
    private long creationDate = 0;
    private List<String> stringList = new ArrayList<>();

    public long getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(long creationDate) {
        this.creationDate = creationDate;
    }

    public List<String> getStringList() {
        return stringList;
    }

    public void setStringList(List<String> stringList) {
        this.stringList = stringList;
    }
}
```

{% endtab %}
{% endtabs %}

## Creating Transformer

Let's first create our custom `StringToListMappingTransformer`;

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class StringToListMappingTransformer : MappingTransformer<String, List<String>> {
    override fun transform(context: MappingTransformerContext<out String>): List<String>? {
        return context.originalValue?.split(",")
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class StringToListMappingTransformer implements MappingTransformer<String, List<String>> {
    @Nullable
    @Override
    public List<String> transform(@NonNull MappingTransformerContext<? extends String> context) {
        return context.getOriginalValue() != null
                ? Arrays.asList(context.getOriginalValue().split(","))
                : null;
    }
}
```

{% endtab %}
{% endtabs %}

The `MappingTransformerContext` holds all the required data to perform simple and complex transformations. In this example, all we need to do is to take the original value and split it.

## Registering Transformers

Since we're using custom transformers, we will have to instantiate ShapeShift using `ShapeShiftBuilder` and define our two transformers. In ShapeShift, you define a transformer by providing a `TransformerRegistration` object.

The registration object is used to define the type of the transformer, its instance, and whether it's a default transformer. We will register the `DateToLongTransformer` as a [default transformer](#default-transformers), and the `StringToListTransformer` as a normal transformer.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val shapeShift = ShapeShiftBuilder()
    .withTransformer(DateToLongMappingTransformer(), default = true)
    .withTransformer(StringToListMappingTransformer())
    .build()
```

{% endtab %}

{% tab title="Java" %}

```java
ShapeShift shapeShift = new ShapeShiftBuilder()
        .withTransformer(new MappingTransformerRegistration(
                Date.class,
                Long.class,
                new DateToLongMappingTransformer(),
                true
        ))
        .withTransformer(new MappingTransformerRegistration(
                String.class,
                List.class,
                new StringToListMappingTransformer(),
                false
        ))
        .build();
```

{% endtab %}
{% endtabs %}

## Default Transformers

When [registering transformers](#registering-transformers) you can indicate wether a transformer is a default transformer. A default transformer of types \<A, B> is used when you map a field of type \<A> to field of type \<B> without specifying a transformer to be used.

ShapeShift comes out of the box with some default transformers. The default transformers are available in the `dev.krud.shapeshift.transformer` package [here](https://github.com/krud-dev/shapeshift/tree/master/shapeshift/src/main/kotlin/dev/krud/shapeshift/transformer).&#x20;

Examples for default transformers: `AnyToStringMappingTransformer`, `DateToLongMappingTransformer`.

### Exclude Default Transformers

To exclude the out of the box default transformers just call the `excludeDefaultTransformers` when creating the ShapeShift instance.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val shapeShift = ShapeShiftBuilder()
    .excludeDefaultTransformers()
    .build()
```

{% endtab %}

{% tab title="Java" %}

```java
ShapeShift shapeShift = new ShapeShiftBuilder()
        .excludeDefaultTransformers()
        .build();
```

{% endtab %}
{% endtabs %}

## Using Transformers

### Annotations

We can now add our annotations;

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
@DefaultMappingTarget(SimpleEntityDisplay::class)
data class SimpleEntity(
    @MappedField
    val creationDate: Date,

    @MappedField(transformer = StringToListMappingTransformer::class, mapTo = "stringList")
    val commaDelimitedString: String
)
```

{% endtab %}

{% tab title="Java" %}

```java
@DefaultMappingTarget(SimpleEntityDisplay.class)
public class SimpleEntity {
    @MappedField
    private Date creationDate;
    @MappedField(transformer = StringToListMappingTransformer.class, mapTo = "stringList")
    private String commaDelimitedString;
    // Getters and Setters...
}
```

{% endtab %}
{% endtabs %}

Note that we did not need to specify a transformer on `creationDate` since the `DateToLongTransformer` is a default transformer for the `Date` type with a `Long` destination type.

### Kotlin DSL

We can create the same mapping with the DSL using the `withTransformer` function.

```kotlin
val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
    SimpleEntity::creationDate mappedTo SimpleEntityDisplay::creationDate
    SimpleEntity::commaDelimitedString mappedTo SimpleEntityDisplay::stringList withTransformer StringToListMappingTransformer::class
}
```

The DSL also supports inline transformer. When we don't need to reuse a transformer we can just add its logic to the DSL.

```kotlin
val mapper = mapper<SimpleEntity, SimpleEntityDisplay> {
    SimpleEntity::creationDate mappedTo SimpleEntityDisplay::creationDate
    SimpleEntity::commaDelimitedString mappedTo SimpleEntityDisplay::stringList withTransformer {
        it.originalValue?.split(",")
    }
}
```

### Java Builder

We can create the same mapping with the builder using the `withTransformer` function.

```java
MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
        .mapField("creationDate", "creationDate")
        .mapField("commaDelimitedString", "stringList")
        .withTransformer(StringToListMappingTransformer.class)
        .build();
```

As the DSL, the builder also supports inline transformer.

```java
MappingDefinition mappingDefinition = new MappingDefinitionBuilder(SimpleEntity.class, SimpleEntityDisplay.class)
        .mapField("creationDate", "creationDate")
        .mapField("commaDelimitedString", "stringList")
        .withTransformer(context -> context.getOriginalValue() != null
                ? Arrays.asList(((String) context.getOriginalValue()).split(","))
                : null)
        .build();
```

## Test

Let's write a test to verify that our mapping is correct;

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
@Test
fun `test mapping for SimpleEntityDisplay`() {
    val shapeShift = ShapeShiftBuilder()
        .withTransformer(DateToLongTransformer(), default = true)
        .withTransformer(StringToCommaSeparatedStringListTransformer())
        .build()
    val simpleEntity = SimpleEntity(
            Date(),
            "one,two,three"
    )
    val result = shapeShift.map<SimpleEntityDisplay>(simpleEntity)
    expectThat(result.creationDate)
            .isEqualTo(simpleEntity.creationDate.time)
    expectThat(result.stringList)
            .isEqualTo(listOf("one", "two", "three"))
}
```

{% endtab %}

{% tab title="Java" %}

```java
@Test
public void testMappingForSimpleEntityDisplay() {
    ShapeShift shapeShift = new ShapeShiftBuilder()
            .excludeDefaultTransformers()
            .withTransformer(new MappingTransformerRegistration(
                    Date.class,
                    Long.class,
                    new DateToLongMappingTransformer(),
                    true
            ))
            .withTransformer(new MappingTransformerRegistration(
                    String.class,
                    List.class,
                    new StringToListMappingTransformer(),
                    false
            ))
            .build();

    SimpleEntity simpleEntity = new SimpleEntity();
    simpleEntity.setCreationDate(new Date());
    simpleEntity.setCommaDelimitedString("one,two,three");

    SimpleEntityDisplay result = shapeShift.map(simpleEntity, SimpleEntityDisplay.class);

    assertEquals(result.getCreationDate(), simpleEntity.getCreationDate().getTime());
    assertEquals(result.getStringList(), Arrays.asList("one", "two", "three"));
}
```

{% endtab %}
{% endtabs %}

## Full Example

You can check out the full example [here](https://github.com/krud-dev/shapeshift/tree/master/example/kotlin/transformer-mapping).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://shapeshift.krud.dev/features/transformers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
