Using JSON in web service calls

Coming in version 4.1.7.

Internally, all calls to Metrici services and data returned from Metrici services is in XML format. However, the web service interface allows Metrici services to be called using JSON and to return responses in JSON.

A distinction can be made between using JSON to call a service designed to use XML (like all the built-in services), and writing a script-based service that is designed around JSON. We will call these "mapped" and "native" JSON calls.

Native JSON call

In native JSON, the JSON string is passed to a script-based service without any processing. This uses a feature of the web service call, in which a POST body of an HTTP request is passed in the "data" parameter if it is not either form data or multipart form data. The script reads the JSON from the data request parameter.

In these cases, you must put the identifier of the script into the URL (either the RunScript or ExecuteScript service end point). In most cases you will want to use the servicejson end point to allow the service to return JSON (see below for details).

For example, you could post to url

https://www.metrici.com/servicejson/ExecuteScript?nodeVersionReference=somecompany.node.reference

with a content type of application/json and a body of

{
"p1":"foo",
"aList":[1,2,3]
}

The JSON would be passed to the script in the <body> request parameter, and can be read in the script using

var json = JSON.parse(application.get('request').getString('body'));

The body cannot hold credentials. Credentials must be implicit (within a browser session), or passed on the query string, or in an Authorization header.

If the body parameter is set, the contentType parameter will also be set, to "application/json".

Native JSON response

You can force the response to be in JSON format by making a call to /servicejson/ServiceName, in place of /service/ServiceName.

This service end point looks for a response parameter with the name "return", and returns that using a content type of application/json.

Within the script, you can create a JSON return simply by putting a javascript object to the return attribute.

application.put('return',{foo:bar,list:[1,2,3]});

Alternatively you can:

  • Set the value of the return attribute to a JSON string.
  • Set the value of the <return> element of the response message to a JSON string.

In each case, the XML response from the service will contain a <return> element that holds the JSON, e.g.

<ExecuteScript>
<errorNumber>0</errorNumber>
<return>{foo:bar,list:[1,2,3]}</return>
</ExecuteScript>

The servicejson end point will return the data in the <return> element.

Returning errors

If the script ends in error, the <return> element will not be interpreted and the response will be interpreted as for the mapped JSON response (see below). This means that the JSON will contain an errorNumber, errorDescription and optional error message, for example:

{
"errorNumber": 102,
"errorDescription": "User not found"
}

If this is not appropriate (for example, because different-named error fields are required), then scripts should trap errors and translate them to an appropriate JavaScript object. For example, the code below traps an application error and returns it using a simple error message in JSON.

if ( application.errorFound() ) {
application.put('return',{
error: application.getErrorMessage(true);
});
application.resetError();
}

The error above would then return:

{
"error": "User not found"
}

Mapped JSON call

Service parameters can be passed in JSON. This is similar to the native JSON call, with the JSON in the body of the request, except that the query string should have the parameter usebody set to true. Mapped JSON calls made to /service/ServiceName will consume JSON and return XML; mapped JSON calls made to /servicejson/ServiceName will consume JSON and return JSON.

For mapped JSON calls, all service parameters (such as the node version reference for script calls, or the credentials) can be held in the body of the JSON request. For example, the service call:

<ExecuteScript>
<userName>someco-admin</userName>
<password>fr978js</password>
<nodeVersionReference>somecompany.data.people</nodeVersionReference>
<action>getUserDetails</action>
<user>jim.jones</user>
<ExecuteScript>

Can be made by posting the following JSON to https://www.metrici.com/service/ExecuteScript?usebody=true

{
"userName": "someco-admin",
"password": "fr978js",
"nodeVersionReference": "somecompany.data.people",
"action": "getUserDetails",
"user": "jim.jones"
}

Credentials can alternatively be passed on the command line or in an authorization header.

Remember to set the content type to application/json when passing in JSON.

The mapping will convert each JSON property containing a simple value into a like-named XML element. The mapping will attempt to convert nested objects and arrays into XML. However, the mechanisms to do so has not been standardised and solutions should consider the treatment of nested objects and arrays as experimental, and be aware it may change in later releases.

Mapped JSON calls would typically be made to /servicejson/ServiceName to force the response to be JSON. If the calls are made to /service/ServiceName, the response will be in XML.

Mapped JSON response

If you do not set the return value, then the /servicejson/ServiceName end point will attempt to convert the response XML to JSON. It will create a JSON property for each top-level element, for example

<Response>
<errorNumber>0</errorNumber>
<userName>Jim Jones</userName>
<email>jim@somewhere.com</email>
</Response>

would become

{
"errorNumber": 0,
"userName":"Jim Jones",
"email":"jim@somewhere.com"
}

If the XML contains nested or repeating elements, an attempt will be made to convert these to JSON. However, the mechanisms to do so has not been standardised and solutions should consider the output from repeating or nested elements as experimental, and be aware it may change in later releases.

If the script returns an error, it will be interpreted using the default rules, even if there is a return element.