Transform JSON with JSONata

Manipulate and transform any JSON document into an easy-to-handle format for your Conversations application. A mapping definition describes how to turn input JSON into output JSON.

Event Type - Mapping Definition Overview

An event type specifies the transformation (mapping) of an external data payload into an API payload suitable for processing by Web1on1, or vice versa. The internal payload for an inbound event should be the Upsert API call for conversations. It contains the contact or conversation identifier(s) and one or more messages to add to the conversation.

Request: POST https://api.web1on1.chat/v2/eventtypes

Authorization: Bearer YOUR-TOKEN
Content-Type: application/json
{
    "name": "tjekvik.checkin.invite",
    "description": "CheckTec checkin",
    "jsonata": "{ \"id\": $conversationForVIN($organizationForBmwId(BMWDealerId), VIN) }"
}

You must create a mapping definition for each JSON document type you want to transform. Then, you can use mapping definitions with the Mappings API to transform JSON documents from one shape into another.

To do the JSON to JSON transformation, we employ a utility called JSONata, which enables us to do the field mapping from external to internal data and vice-versa.

What is JSONata?

JSONata is a powerful data transformation and manipulation language for JSON data. The runtime library written in JavaScript was originally developed by IBM and later open sourced. JSONata is especially useful when dealing with APIs that return JSON data with different structures, as it can transform the data into a JSON format that is compatible with other APIs.

The JSONata documentation includes many examples for what you do with it. Visit the JSONata Exerciser to verify and/or practice writing JSONata expressions.

Why JSONata in CitNOW Conversations

JSONata assists in manipulating and converting source JSON data into a preferred target format, enabling pain-free deserialization in CN Conversations. The key point is the "preferred target format", since JSONata's capabilities extend beyond addressing issues with source JSON data, enabling the transformation of any JSON data into any desired format. This includes a wide array of additional transformations and manipulations, such as data concatenation, element filtering, various calculations, and much more.

Leveraging JSONata for transformation and manipulation of JSON data can help to reduce ETL programming code inside and outside CN Conversations drastically.

How to use JSONata

Using JSONata is quite simple. You need a source JSON document and a JSONata definition.

A JSONata definition contains all the instructions on how you want to transform and manipulate your source document.

To create and try out a JSONata definition you can use the JSONata Exerciser before using it in your application. JSONata is immensely powerful, and the learning curve can be steep. In this article, I want to give some first, but simple, examples to get up and running with JSONata transformations.

More information on creating transformation and manipulation definitions can be found in the official documentation at www.jsonata.org.

Mapping expression

A mapping expression is a piece of code that specifies how to turn input fields into an output field. The following example shows how you could output a total price from a quantity and a unit price.

{ "total": item.quantity * item.unit_price }

On the left is the name of the output field, on the right is the mapping expression, written in JSONata. You can add more output fields, separated by commas.

{ "total": item.quantity * item.unit_price, "currency": item.currency, "product": product.id }

The result looks just like JSON, even though it technically isn’t, because of the syntax of JSONata.

JSONata Transformations

The Events API takes the incoming message body and applies the configured JSONata transformation on it. It uses a fact that JSONata expression is a superset of JSON document so that by default any valid JSON document is a valid JSONata expression.

For Inbound Events, the JSONata transformation should result in the payload for a conversation Upsert API call. For Outbound Events the JSONata transformation should result in a payload conforming to the external endpoint's supported schema.

Example: Inbound Events

Inbound Events specify transformation from anything to Conversation Upsert payload. As an example, let's take the following incoming message body.

Source JSON

Our source JSON document looks like below. When POSTing the source payload as an event, the transformation output document will Upsert (create or update) a conversation.

Request: POST https://api.web1on1.chat/v2/events/acme.workorder.ready

{
    "dealerId": "kangaroo-647",
    "contact": {
        "FirstName": "John",
        "Surname": "Johnson",
        "Phone": [{ "type": "mobile", "number": "+441172345678" }],
        "Email": [{ "type": "office", "address": "john@example.com" }],
    }
}

Expected JSON

The result of the transformation should look similar to the following CNC JSON document:

{
    "organization": "66c3ed372a33131f1b4ba1b8",
    "type": "contact",
    "contact": {
        "profile": {
            "givenName": "John",
            "familyName": "Johnson",
            "telephone": "+441172345678",
            "email": "john@example.com"
        }
    },
    "messages": [
        {
            "type": "status",
            "text": "Acme Workorder Ready"
        }
    ]
}

JSONata Expression

To quickly iterate on the schema mapping, issue PUT requests to the event type. Make sure the content-type is text/plain, and the resulting JSON matches a typical conversation upsert request.

Request: PUT https://api.web1on1.chat/v2/eventtypes/6663a1f684ba342816c4b7c4/jsonata

{
    "organization": $organizationForExternalId(dealerId),
    "type": "contact",
    "contact": {
        "profile": {
            "givenName": contact.FirstName,
            "familyName": contact.Surname,
            "telephone": contact.Phone[type = "mobile"].number,
            "email": contact.Email[0].address
        }
    },
    "messages": [
        {
            "type": "status",
            "text": "Acme Workorder Ready"
        }
    ]
}

Our definition begins with “{“ which indicates that the result should be a JSON object. Within the object, we define the attributes "organization", "type", "contact" and "messages", required when a conversation ID can not be derived. Then we reference the “fn” attribute from the source document (the assumption is that we always have an “fn” attribute and it has a value). We complete the definition with “}” to close the JSON object.

The example above should give you a quick overview of the possibilities you get with JSONata transformations. Check the JSONata documentation for additional transformation and manipulation capabilities. You can find and tryout many complex expressions in the JSONata playground.

Outbound Events

Native CNC event payloads can be shaped to your specification before they reach your webhook URL. Specify webhook payload transformations to any target format by configuring the service.jsonata property.

Special lookup functions

  • $organizationForBmwId(id)
  • $organizationForExternalId(id)
  • $locationForExternalId(id)
  • $departmentForExternalId(id)
  • $conversationForVIN(organization, vin)
  • $conversationForLicensePlate(organization, licensePlate)
  • $normalizePhone(organization, phoneNumber)