# Criando transformações JOLT avançadas

O JOLT pode ser usado para diversos tipos de transformações, das mais básicas às mais avançadas. Neste documento, exploramos **operações avançadas**, que permitem:

* Converter objetos em listas (e vice-versa).
* Inserir valores padrão ou substituir valores existentes.
* Aplicar funções de string, numéricas, de conversão de tipo e de listas diretamente durante a transformação.

{% hint style="success" %}
**Dica:** Teste os exemplos deste documento no [playground do JOLT](https://jolt-demo.appspot.com/#inception).
{% endhint %}

## **Entendendo as operações avançadas do JOLT**

Nesta seção, você aprenderá como cada operação avançada do JOLT funciona — `cardinality`, `modify-overwrite-beta` e `modify-default-beta` — e quando usá-las.

### **cardinality**

A operação `cardinality` é usada para converter um objeto simples em uma lista de objetos ou transformar uma lista de volta em um único objeto.

Ao converter uma lista em um objeto único, apenas o primeiro elemento da lista é preservado.

#### **Exemplo 1 — Convertendo um objeto em uma lista**

* **Caso de uso:** Padronizar a estrutura dos dados de produto para que eles sempre apareçam como uma lista, mesmo quando apenas um item é fornecido.
* **Objetivo:** Converter o objeto `products` em um array contendo um único elemento.

**JSON de entrada**

```json
{
 "products": {
   "name": "Product A",
   "id": "123-A",
   "value": 10
 }
}
```

**JSON de saída desejado**

```json
{
 "products": [
   {
     "name": "Product A",
     "id": "123-A",
     "value": 10
   }
 ]
}
```

**Spec de transformação**

```json
[
 {
   "operation": "cardinality",
   "spec": {
     "products": "MANY"
   }
 }
]
```

**Explicação**

A operação `cardinality` altera como o JOLT interpreta a estrutura de um campo. Ao definir `"products": "MANY"`, indicamos ao JOLT que `products` deve **sempre ser tratado como uma lista**, mesmo que a entrada contenha apenas um único objeto.

O JOLT então envolve o objeto `products` existente dentro de um array, produzindo uma estrutura de lista consistente na saída. Isso é útil quando o payload às vezes inclui um item e às vezes muitos, mas a integração exige que o campo **sempre** se comporte como uma lista.

#### **Exemplo 2 — Convertendo uma lista em um objeto simples**

* **Caso de uso:** Simplificar respostas de produto convertendo listas em um único objeto quando apenas o primeiro item é relevante para o processamento posterior.
* **Objetivo:** Transformar o array `products` em um único objeto contendo apenas seu primeiro elemento.

**JSON de entrada**

```json
{
 "products": [
   {
     "name": "Product A",
     "id": "123-A",
     "value": 10
   },
   {
     "name": "Product B",
     "id": "456-B",
     "value": 20
   }
 ]
}
```

**JSON de saída desejado**

```json
{
 "products": {
   "name": "Product A",
   "id": "123-A",
   "value": 10
 }
}
```

**Spec de transformação**

```json
[
 {
   "operation": "cardinality",
   "spec": {
     "products": "ONE"
   }
 }
]
```

**Explicação**

A operação `cardinality` permite controlar se um campo deve ser tratado como um **objeto único** ou uma **lista**. Ao definir `"products": "ONE"`, informamos ao JOLT que o campo `products` deve ser interpretado como **um único objeto**, mesmo quando a entrada fornece uma lista.

O JOLT então pega apenas o **primeiro elemento** do array e o retorna como um objeto. Isso é útil quando você espera apenas um item, mas a entrada pode, às vezes, chegar dentro de uma lista.

### **modify-default-beta e modify-overwrite-beta**

Essas operações permitem referenciar valores de forma dinâmica dentro do JSON. A diferença entre elas é:

* `modify-default-beta` adiciona um valor somente se o campo ainda não existir.
* `modify-overwrite-beta` sempre substitui o valor do campo, independentemente de ele existir. Além disso, ela permite aplicar funções para transformar valores no JSON.

Essas funções são agrupadas nas seguintes categorias:

**Funções de string**

* `toLower`, `toUpper`, `concat`, `join`, `split`, `substring`, `trim`, `leftPad`, `rightPad`

**Funções numéricas**

* `min`, `max`, `abs`, `avg`, `intSum`, `doubleSum`, `longSum`, `intSubtract`, `doubleSubtract`, `longSubtract`, `divide`, `divideAndRound`

**Funções de tipo**

* `toInteger`, `toDouble`, `toLong`, `toBoolean`, `toString`
* `recursivelySquashNulls`, `squashNulls`, `size`

**Funções para listas**

* `firstElement`, `lastElement`, `elementAt`, `toList`, `sort`

#### **Exemplo prático**

* **Caso de uso:** Aplicar múltiplas transformações para enriquecer e normalizar um JSON complexo contendo strings, números, listas e tipos mistos.
* **Objetivo:** Gerar uma saída refinada formatando textos, manipulando valores numéricos, convertendo tipos, removendo campos nulos, extraindo elementos de listas e ordenando arrays — tudo em uma única transformação.

**JSON de entrada**

{% code expandable="true" %}

```json
{
 "STRING": {
   "product": "Product A",
   "company": "company a",
   "value": "100",
   "measureWithSpaces": "  10 meters "
 },
 "NUMBER": {
   "array": [ 3, 5, 2, 7, 1 ],
   "negativeValue": -100,
   "positiveValue": 50
 },
 "TYPE": {
   "value": 10.5,
   "stringBoolean": "true",
   "objectWithNull": {
     "fielWithValue": "ABC",
     "nullField": null
   }
 },
 "LIST": {
   "array": [ "c", "t", "m", "a" ],
   "stringField": "123"
 }
}
```

{% endcode %}

**JSON de saída desejado**

{% code expandable="true" %}

```json
{
  "STRING" : {
    "product" : "product a",
    "company" : "COMPANY A",
    "value" : "AAA100",
    "measureWithSpaces" : "  10 meters ",
    "product_company" : "product a_COMPANY A",
    "joinProductCompany" : "product a - COMPANY A",
    "splitProductCompany" : [ "product a ", " COMPANY A" ],
    "substringProduct" : "prod",
    "measure" : "10 meters"
  },
  "NUMBER" : {
    "array" : [ 3, 5, 2, 7, 1 ],
    "negativeValue" : -100,
    "positiveValue" : 50,
    "minArray" : 1,
    "maxArray" : 7,
    "absoluteValue" : 100,
    "averageArray" : 3.6,
    "sumArray" : 18,
    "subtrArray" : 30,
    "division" : 25.0,
    "divisionRound" : 16.667
  },
  "TYPE" : {
    "value" : 10.5,
    "stringBoolean" : 4,
    "objectWithNull" : {
      "fielWithValue" : "ABC"
    },
    "integerValue" : 10,
    "booleano" : true,
    "stringValue" : "10.5"
  },
  "LIST" : {
    "array" : [ "c", "t", "m", "a" ],
    "stringField" : "123",
    "arrayFirstItem" : "c",
    "arrayLastItem" : "a",
    "fieldToList" : [ "123" ],
    "orderedArray" : [ "a", "c", "m", "t" ]
  }
}
```

{% endcode %}

**Spec de transformação**

{% code expandable="true" %}

```json
[
 {
   "operation": "modify-overwrite-beta",
   "spec": {
     "STRING": {
       "product": "=toLower(@(1,product))",
       "company": "=toUpper(@(1,company))",
       "product_company": "=concat(@(1,product),'_',@(1,company))",
       "joinProductCompany": "=join(' - ',@(1,product),@(1,company))",
       "splitProductCompany": "=split('[-]',@(1,joinProductCompany))",
       "substringProduct": "=substring(@(1,product),0,4)",
       "value": "=leftPad(@(1,value),6,'A')",
       "measure": "=trim(@(1,measureWithSpaces))"
     },
     "NUMBER": {
       "minArray": "=min(@(1,array))",
       "maxArray": "=max(@(1,array))",
       "absoluteValue": "=abs(@(1,negativeValue))",
       "averageArray": "=avg(@(1,array))",
       "sumArray": "=intSum(@(1,array))",
       "subtrArray": "=intSubtract(@(1,positiveValue), 20)",
       "division": "=divide(@(1,positiveValue),2)",
       "divisionRound": "=divideAndRound(3,@(1,positiveValue),3)"
     },
     "TYPE": {
       "integerValue": "=toInteger(@(1,value))",
       "booleano": "=toBoolean(@(1,stringBoolean))",
       "stringValue": "=toString(@(1,value))",
       "stringBoolean": "=size",
       "objectWithNull": "=recursivelySquashNulls"
     },
     "LIST": {
       "arrayFirstItem": "=firstElement(@(1,array))",
       "arrayLastItem": "=lastElement(@(1,array))",
       "arrayElement": "=elementAt(@(1,array),2)",
       "fieldToList": "=toList(@(1,stringField))",
       "orderedArray": "=sort(@(1,array))"
     }
   }
 }
]
```

{% endcode %}

**Explicação**

Este exemplo usa a operação `modify-overwrite-beta` para aplicar **múltiplas transformações em diferentes tipos de dados**, tudo em uma única etapa. A transformação faz o seguinte:

**Seção STRING**

* Converte valores de string para **letras minúsculas** e **maiúsculas**.
* Cria novos campos por meio de **concatenação**, **junção (join)** e **separação (split)** de valores existentes.
* Extrai substrings usando `substring`.
* Preenche valores com caracteres usando `leftPad`.
* Remove espaços extras com `trim`.

**Seção NUMBER**

* Calcula **mínimo**, **máximo**, **soma**, **subtração**, **média** e **valor absoluto**.
* Realiza **divisão** e **divisão arredondada** com `divideAndRound`.

**Seção TYPE**

* Converte valores para **integer**, **boolean** e **string**.
* Remove campos `null` dentro de objetos aninhados usando `recursivelySquashNulls`.
* Substitui o campo `"stringBoolean"` pelo seu **tamanho**.

**Seção LIST**

* Extrai o **primeiro**, **último** e elementos específicos de um array.
* Converte um único campo em uma **lista** usando `toList`.
* Ordena valores de array alfabeticamente com `sort`.

No geral, esta transformação demonstra como `modify-overwrite-beta` pode ser usada para enriquecer dados, limpar campos, calcular valores numéricos, normalizar tipos e organizar listas — tudo em uma única operação.

#### **Notas adicionais**

Algumas funções não aparecem no exemplo de transformação porque seguem o mesmo padrão de uso. Por exemplo, `doubleSum` e `longSum` funcionam exatamente como `intSum`.

Sobre as funções de tratamento de nulos:

* **`recursivelySquashNulls`** remove **todos** os campos com valores nulos em **qualquer** nível de profundidade.

```json
[
{
    	"operation": "modify-overwrite-beta",
    	"spec": {
      		"*": "=recursivelySquashNulls"
    	}
  }
]
```

* **`squashNulls`** remove campos nulos apenas **um nível abaixo** do objeto ou lista atual.

```json
[
  {
    "operation": "modify-overwrite-beta",
    "spec": {
      "product": "=squashNulls"
    }
  }
]
```

#### **Comportamento em cascata**

A operação `modify-overwrite-beta` aplica as transformações na ordem em que aparecem. Isso significa que cada transformação pode utilizar os valores produzidos pelas anteriores — um comportamento conhecido como **cascata**.

O exemplo a seguir demonstra isso de forma clara.

**JSON de entrada**

```json
{
  "name": "MARIA",
  "country": "brazil"
}
```

**Spec de transformação**

```json
[
  {
    "operation": "modify-overwrite-beta",
    "spec": {
      "name": "=toLower",
      "country": "=toLower"
    }
  },
  {
    "operation": "modify-overwrite-beta",
    "spec": {
      "name_country": "=concat(@(1,name),'_',@(1,country))"
    }
  }
]
```

**Como funciona**

1. **Primeira operação:**\
   Tanto `name` quanto `country` são transformados no próprio campo.
   * `"MARIA"` se torna `"maria"`
   * `"brazil"` permanece `"brazil"`
2. **Segunda operação:**\
   A transformação cria o campo `name_country` concatenando os valores **já atualizados** de `name` e `country`. Como a primeira operação já foi concluída, a função recebe os valores transformados.

**Resultado**

```json
{
  "name": "maria",
  "country": "brazil",
  "name_country": "maria_brazil"
}
```

## **Continue aprendendo**

Agora que você explorou as operações avançadas, veja como esses conceitos funcionam na prática acessando nossos [**exemplos de casos de uso**](https://docs.digibee.com/documentation/resources/pt-br/use-cases/how-to-jolt/use-cases), onde cada transformação é aplicada a cenários reais de integração.
