Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Table of Contents
maxLevel3

Also see the following sections in the WSO2 ESB documentation. WSO2 ESB is used to implement the API Gateway through which API messages are transformed:

JSON message builders and formatters

...

Code Block
languagehtml/xml
linenumberstrue
<proxy <api xmlns="http://ws.apache.org/ns/synapse"
      name="tojson"
 name="admin--TOJSON" context="/tojson" version="1.0" version-type="url">
       transports <resource methods="https,http"POST GET DELETE OPTIONS    statisticsPUT" url-mapping="disable/*">
      trace="disable"      <inSequence>
startOnLoad="true">   <target>      <inSequence>         <property name="messageTypePOST_TO_URI" value="application/jsontrue" scope="axis2"/>
        <respond/>      </inSequence>   </target>
  <description/>
</proxy>

Use the following command to invoke this proxy service:

Code Block
languagebash
curl -v -X POST -H "Content-Type:application/xml" -d@request1.xml "http://localhost:8280/services/tojson"

If the request payload is as follows:

Code Block
languagehtml/xml
<coordinates>
   <location><property name="messageType" value="application/json" scope="axis2"/>
                <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                   <name>Bermuda Triangle</name><then>
       <n>25.0000</n>        <w>71.0000</w>    </location>    <location> <send>
      <name>Eiffel Tower</name>        <n>48.8582</n>        <e>2.2945</e>    </location>
</coordinates>

The response payload will look like this:

Code Block
languagejavascript
{
  "coordinates":{ <endpoint name="admin--Test_APIproductionEndpoint_0">
            "location":[         {           <http "name":"Bermuda Triangle",uri-template="http://localhost:9767/services/StudentService">
             "n":25.0000,            "w":71.0000         },  <timeout>
      {            "name":"Eiffel Tower",               "n":48.8582,       <duration>30000</duration>
    "e":2.2945         }      ]   } }

Note that we have used the Property mediator to mark the outgoing payload to be formatted as JSON. For more information about the Property Mediator, see the Property Mediator page on WSO2 ESB documentation.

Code Block
languagehtml/xml
<property name="messageType" value="application/json" scope="axis2"/>

Accessing content from JSON payloads

There are two ways to access the content of a JSON payload:

  • JSONPath expressions (with json-eval() method)

  • XPath expressions

JSONPath allows you to access fields of JSON payloads with faster results and less processing overhead. Although it is possible to evaluate XPath expressions on JSON payloads by assuming the XML representation of the JSON payload, we recommend that you use JSONPath to query JSON payloads. It is also possible to evaluate both JSONPath and XPath expressions on a payload (XML/JSON) at the same time.

You can use JSON path expressions with following mediators:

...

As a log property:

...

languagehtml/xml

...

                 <responseAction>fault</responseAction>
                                    </timeout>
                                    <suspendOnFailure>
                                        <errorCodes>-1</errorCodes>
                            

...

As a standalone property:

...

languagehtml/xml

...

            <initialDuration>0</initialDuration>
                              

...

As the payload arguments:

...

languagehtml/xml

...

          <progressionFactor>1.0</progressionFactor>
     

...

     

...

         

...

                     <maximumDuration>0</maximumDuration>
                                    </

...

IMPORTANT: You MUST omit the json-eval() method within the payload arguments to evaluate JSON paths within the PayloadFactory mediator. Instead, you MUST select the correct expression evaluator (xml or json) for a given argument.

...

As the switch source:

Code Block
languagehtml/xml
<switch source="json-eval($.coordinates.location[0].name)">

...

As the filter source:

...

languagehtml/xml

...

suspendOnFailure>
                           

...

JSON path syntax

Suppose we have the following payload:

Code Block
languagejavascript
    "id": 12345,   "id_str": "12345", <markForSuspension>
  "array": [ 1, 2, [ [], [{"inner_id": 6789}] ] ],       "name": null,   "object": {},   "$schema_location": "unknown",   "12X12": "image12x12.png" }

The following table summarizes sample JSONPath expressions and their outputs:

...

$.

...

 

...

 

...

 

...

$.id

...

12345

...

$.name

...

null

...

$.object

...

{}

...

$.['$schema_location']

...

unknown

...

$.12X12

...

image12x12.png

...

$.array

...

[1, 2, [[],[{"inner_id":6789}]]]

...

$.array[2][1][0].inner_id

...

6789

You can learn more about JSONPath syntax here.

Logging JSON payloads

To log JSON payloads as JSON, use the Log mediator as shown below. The json-eval() method returns the java.lang.String representation of the existing JSON payload.

Code Block
languagehtml/xml
<log>
    <property name="JSON-Payload" expression="json-eval($.)"/>
</log>

To log JSON payloads as XML, use the Log mediator as shown below:

Code Block
languagehtml/xml
<log level="full"/>

For more information on logging, see Troubleshooting, debugging, and logging below.

Constructing and transforming JSON payloads

To construct and transform JSON payloads, you can use the PayloadFactory mediator or Script mediator as described in the rest of this section.

PayloadFactory mediator

The PayloadFactory mediator provides the simplest way to work with JSON payloads. For information on the mediator, see WSO2 ESB documentation: https://docs.wso2.org/display/ESB481/PayloadFactory+Mediator. Suppose we have a service that returns the following response for a search query:

Code Block
languagejavascript
{
   "geometry":{
      "location":{        <errorCodes>-1</errorCodes>
                                    </markForSuspension>
                                </http>
                            </endpoint>
                        </send>
                    </then>
                    <else>
               "lat":-33.867260,         <sequence "lng":151.1958130key="_sandbox_key_error_"/>
         }    },    "icon":"bar-71.png",    "id":"7eaf7",</else>
     "name":"Biaggio Cafe",    "opening_hours":{       "open_now":true</filter>
    },    "photos":[    </inSequence>
  {          "height":600,<outSequence>
         "html_attributions":[       <send/>
  ],          "photo_reference":"CoQBegAAAI",</outSequence>
         "width":900
</resource>
      }  <handlers>
 ],    "price_level":1,    "reference":"CnRqAAAAtz",   <handler "types":[class="org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler"/>
      "bar",      <handler "restaurant",class="org.wso2.carbon.apimgt.gateway.handlers.throttling.APIThrottleHandler">
                <property name="id" value="food",A"/>
         "establishment"    ],   <property "vicinity":"48 Pirrama Road, Pyrmont"
}

We can create a proxy service that consumes the above response and creates a new response containing the location name and tags associated with the location based on several fields from the above response.

Code Block
languagehtml/xml
linenumberstrue
<proxy xmlns="http://ws.apache.org/ns/synapse"name="policyKey" value="gov:/apimgt/applicationdata/tiers.xml"/>
            </handler>
      name="singleresponse"      transports="https,http"
     statistics="disable"<handler class="org.wso2.carbon.apimgt.usage.publisher.APIMgtUsageHandler"/>
     trace="disable"       <handler startOnLoadclass="true"org.wso2.carbon.apimgt.usage.publisher.APIMgtGoogleAnalyticsTrackingHandler"/>
     <target>          <outSequence>
             <payloadFactory media-type="json"<handler class="org.wso2.carbon.apimgt.gateway.handlers.ext.APIManagerExtensionHandler"/>
        </handlers>
        <format>{
                             "location_response" : {
      </api>

An example command to invoke above API:

Code Block
languagebash
curl -v -X POST -H "Content-Type:application/xml" -H "Authorization: Bearer xxx" -d@request1.xml "http://10.100.1.110:8280/tojson/1.0"

If the request payload is as follows:

Code Block
languagehtml/xml
<coordinates>
   <location>
       <name>Bermuda Triangle</name>
       <n>25.0000</n>
       <w>71.0000</w>
 "name" : "$1", </location>
   <location>
       <name>Eiffel Tower</name>
       <n>48.8582</n>
       <e>2.2945</e>
    "tags" : $2
 </location>
</coordinates>

The response payload will look like this:

Code Block
languagejavascript
{
  "coordinates":{
     "location":[
        {
        }}   "name":"Bermuda Triangle",
           "n":25.0000,
 </format>          "w":71.0000
       <args> },
        {
           <arg evaluator="json" expression="$.name"/>"name":"Eiffel Tower",
                     <arg evaluator="json" expression="$.types"/>"n":48.8582,
           "e":2.2945
     </args>   }
     ]
    </payloadFactory>
             <send/>
         </outSequence>
         <endpoint>
             <address uri="http://localhost:82801/location"/>
 }
}

Note that we have used the Property mediator to mark the outgoing payload to be formatted as JSON. For more information about the Property Mediator, see the Property Mediator page on WSO2 ESB documentation.

Code Block
languagehtml/xml
<property name="messageType" value="application/json" scope="axis2"/>

Similarly if the response message needs to be transformed, set the messageType property in the outSequence.

Code Block
languagehtml/xml
 <api name="admin--TOJSON" context="/tojson" version="1.0" version-type="url">
       </endpoint> <resource methods="POST GET DELETE OPTIONS </target>
 <description/>
</proxy>

Use the following command to invoke this service:

Code Block
languagebash
curl -v -X GET "http://localhost:8280/services/singleresponse"

The response payload would look like this:

Code Block
languagejavascript
{
   "location_response":{
 PUT" url-mapping="/*">
            <inSequence>
    "name":"Biaggio Cafe",       "tags":["bar", "restaurant", "food", "establishment"]
   }
}

Note the following aspects of the proxy service configuration:

  • We use the payloadFactory mediator to construct the new JSON payload.
  • The media-type attribute is set to json.
  • Because JSONPath expressions are used in arguments, the json evaluators are specified.
Configuring the payload format

The <format> section of the proxy service configuration defines the format of the response. Notice that in the example above, the name and tags field values are enclosed by double quotes ("), which creates a string value in the final response. If you do not use quotes, the value that gets assigned uses the real type evaluated by the expression (boolean, number, object, array, or null).

It is also possible to instruct the PayloadFactory mediator to load a payload format definition from the registry. This approach is particularly useful when using large/complex payload formats in the definitions. To load a format from the registry, click Pick From Registry instead of Define inline when defining the PayloadFactory mediator.

For example, suppose we have saved the following text content in the registry under the location conf:/repository/esb/transform. (The resource name is “transform”.)

Code Block
languagehtml/xml
linenumberstrue
{
    "location_response" : {
        "name" : "$1",
        "tags" : $2
    }
}

We can now modify the definition of the PayloadFactory mediator to use this format text saved as a registry resource as the payload format. The new configuration would look as follows (note that the <format> element now uses the key attribute to point to the registry resource key):

Code Block
languagehtml/xml
<payloadFactory media-type="json">
  <format key="conf:/repository/esb/transform"/>
  ... 
</payloadFactory>
Note

When saving format text for the PayloadFactory mediator as a registry resource, be sure to save it as text content with the “text/plain” media type.

Script mediator

The Script mediator (https://docs.wso2.org/display/ESB481/Script+Mediator) in JavaScript is useful when you need to create payloads that have recurring structures such as arrays of objects. The Script mediator defines the following important methods that can be used to manipulate payloads in many different ways:

  • getPayloadJSON
  • setPayloadJSON
  • getPayloadXML
  • setPayloadXML

By combining any of the setters with a getter, we can handle almost any type of content transformation. For example, by combining getPayloadXML and setPayloadJSON, we can easily implement an XML to JSON transformation scenario. In addition, we can perform various operations (such as deleting individual keys, modifying selected values, and inserting new objects) on JSON payloads to transform from one JSON format to another JSON format by using the getPayloadJSON and setPayloadJSON methods. Following is an example of a JSON to JSON transformation performed by the Script mediator.

Suppose a second service returns the following response:

Code Block
languagejavascript
{
   "results" : [
      {
         "geometry" : {
            "location" : {
               "lat" : -33.867260,
               "lng" : 151.1958130
            }
         },
         "icon" : "bar-71.png",
         "id" : "7eaf7",
         "name" : "Biaggio Cafe",
         "opening_hours" : {
            "open_now" : true
         },
         "photos" : [
            {
               "height" : 600,
               "html_attributions" : [],
               "photo_reference" : "CoQBegAAAI",
               "width" : 900
            }
         ],
         "price_level" : 1,
         "reference" : "CnRqAAAAtz",
         "types" : [ "bar", "restaurant", "food", "establishment" ],
         "vicinity" : "48 Pirrama Road, Pyrmont"
      },
      {
         "geometry" : {
            "location" : {
               "lat" : -33.8668040,
               "lng" : 151.1955790
            }
         },
         "icon" : "generic_business-71.png",
         "id" : "3ef98",
         "name" : "Doltone House",
         "photos" : [
            {
               "height" : 600,
               "html_attributions" : [],
               "photo_reference" : "CqQBmgAAAL",
               "width" : 900
            }
         ],
         "reference" : "CnRrAAAAV",
         "types" : [ "food", "establishment" ],
         "vicinity" : "48 Pirrama Road, Pyrmont"
      }
   ],
   "status" : "OK"
}

The following proxy service shows how we can transform the above response using JavaScript with the Script mediator.

Code Block
languagehtml/xml
linenumberstrue
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="locations"
       transports="https,http"
       statistics="disable"
       trace="disable"
       startOnLoad="true">
   <target>
      <outSequence>
         <script language="js"
                 key="conf:/repository/esb/transform.js"
                 function="transform"/>
         <send/>
      </outSequence>
      <endpoint>
         <address uri="http://localhost:82801/locations"/>
      </endpoint>
   </target>
   <description/>
</proxy>

The registry resource transform.js contains the JavaScript function that performs the transformation:

Code Block
languagejavascript
linenumberstrue
function transform(mc) {
    payload = mc.getPayloadJSON();
    results = payload.results;
    var response = new Array();
    for (i = 0; i < results.length; ++i) {
        location_object = results[i];
        l = new Object();
        l.name = location_object.name;
        l.tags = location_object.types;
        l.id = "ID:" + (location_object.id);
        response[i] = l;
    }
    mc.setPayloadJSON(response);
}

...

Code Block
languagebash
curl -v -X GET "http://ggrky:8280/services/locations"

The response payload would look like this:

Code Block
languagejavascript
[
    {    <property name="POST_TO_URI" value="true" scope="axis2"/>
                <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                    <then>
                        <send>
                            <endpoint name="admin--Test_APIproductionEndpoint_0">
                                <http uri-template="http://localhost:9767/services/StudentService">
                                   "id":"ID:7eaf7",  <timeout>
        "tags":["bar", "restaurant", "food", "establishment"],          "name":"Biaggio Cafe"     },      {        "id":"ID:3ef98",<duration>30000</duration>
             "tags":["food", "establishment"],         "name":"Doltone House"     } ]

If you want to get the response in XML instead of JSON, you would modify the out sequence by adding the Property mediator as follows:

Code Block
languagehtml/xml
linenumberstrue
<outSequence>     <script language="js"    <responseAction>fault</responseAction>
         key="conf:/repository/esb/transform.js"              function="transform"/>     <property name="messageType" value="application/xml" scope="axis2"/>     <send</>
</outSequence>

The response will then look like this:

Code Block
languagehtml/xml
<jsonArray>timeout>
     <jsonElement>       <id>ID:7eaf7</id>       <tags>bar</tags>       <tags>restaurant</tags>       <tags>food</tags>   <suspendOnFailure>
   <tags>establishment</tags>       <name>Biaggio Cafe</name>    </jsonElement>    <jsonElement>       <id>ID:3ef98</id>       <tags>food</tags>       <tags>establishment<<errorCodes>-1</tags>errorCodes>
      <name>Doltone House</name>    </jsonElement>
</jsonArray>

If you are not getting the results you want when the Script mediator converts the JSON payload directly into XML, you can build the XML payload iteratively with the Script mediator as shown in the following script.

Code Block
languagejavascript
linenumberstrue
function transformXML(mc) {            payload = mc.getPayloadJSON();     results = payload.results;     var response = <locations/>;<initialDuration>0</initialDuration>
                              for (i = 0; i < results.length; ++i) {  <progressionFactor>1.0</progressionFactor>
      var elem = results[i];         response.locations += <location>             <id>{elem.id}</id>       <maximumDuration>0</maximumDuration>
     <name>{elem.name}</name>             <tags>{elem.types}</tags>         </location>     }     mc.setPayloadXML(response);
}

The response would now look like this:

Code Block
languagehtml/xml
<locations></suspendOnFailure>
   <location>       <id>7eaf7</id>       <name>Biaggio Cafe</name>       <tags>bar,restaurant,food,establishment</tags>    </location>    <location>    <markForSuspension>
  <id>3ef98</id>       <name>Doltone House</name>       <tags>food,establishment</tags>    </location>
</locations>

Finally, let's look at how you can perform delete, modify, and add field operations on JSON payloads with the Script mediator in JavaScript. Let's send the JSON message returned by the locations proxy service as the request for the following proxy service, transformjson:

Code Block
languagehtml/xml
linenumberstrue
<proxy xmlns="http://ws.apache.org/ns/synapse"                   <errorCodes>-1</errorCodes>
                name="transformjson"        transports="https,http"        statistics="disable"    </markForSuspension>
    trace="disable"         startOnLoad="true">    <target>       <inSequence>        </http>
 <script language="js"><![CDATA[            payload = mc.getPayloadJSON();            for</endpoint>
(i = 0; i &lt; payload.length; ++i) {                payload[i].id_str = payload[i].id; </send>
                delete payload[i].id;   </then>
            payload[i].tags[payload[i].tags.length] = "pub";      <else>
     }            mc.setPayloadJSON(payload);       <sequence key="_sandbox_key_error_"/>
 ]]></script>          <log>         </else>
   <property name="JSON-Payload" expression="json-eval($.)"/>            </log>filter>
            <respond/></inSequence>
          </inSequence>  <outSequence>
 </target>    <description/> </proxy>

The proxy service will convert the request into the following format:

Code Block
languagejavascript
[    {      <property "name":"Biaggio Cafe",name="messageType" value="application/json" scope="axis2"/>
          "tags":["bar", "restaurant", "food", "establishment", "pub"],      <send/>
      "id_str":"ID:7eaf7"    },  </outSequence>
 {       "name":"Doltone House",</resource>
       "tags":["food", "establishment", "pub"], <handlers>
       "id_str":"ID:3ef98"     }
]

Note that the transformation (line 9 through 17) has added a new field id_str and removed the old field id from the request, and it has added a new tag pub to the existing tags list of the payload.

...

Troubleshooting, debugging, and logging

To assist with troubleshooting, you can enable debug logging at several stages of the mediation of a JSON payload by adding one or more of the following loggers to the <PRODUCT_HOME>/repository/conf/log4j.properties file and restarting the server.

Info

Be sure to turn off these loggers when running the server in a production environment, as logging every message will significantly reduce performance.

Following are the available loggers:

Message builders and formatters
  • log4j.logger.org.apache.synapse.commons.json.JsonStreamBuilder=DEBUG
  • log4j.logger.org.apache.synapse.commons.json.JsonStreamFormatter=DEBUG
  • log4j.logger.org.apache.synapse.commons.json.JsonBuilder=DEBUG
  • log4j.logger.org.apache.synapse.commons.json.JsonFormatter=DEBUG
JSON utility class

log4j.logger.org.apache.synapse.commons.json.JsonUtil=DEBUG

PayloadFactory mediator

log4j.logger.org.apache.synapse.mediators.transform.PayloadFactoryMediator=DEBUG

JSONPath evaluator

...

<handler class="org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler"/>
            <handler class="org.wso2.carbon.apimgt.gateway.handlers.throttling.APIThrottleHandler">
                <property name="id" value="A"/>
                <property name="policyKey" value="gov:/apimgt/applicationdata/tiers.xml"/>
            </handler>
            <handler class="org.wso2.carbon.apimgt.usage.publisher.APIMgtUsageHandler"/>
            <handler class="org.wso2.carbon.apimgt.usage.publisher.APIMgtGoogleAnalyticsTrackingHandler"/>
            <handler class="org.wso2.carbon.apimgt.gateway.handlers.ext.APIManagerExtensionHandler"/>
        </handlers>
    </api>