Genivia Home Developer Center

XML-RPC & JSON

 
XML-RPC and JSON/JSONPath

Table of Contents

Introduction

XML-RPC predates JSON and shares the same goal to offer a simple data format for Web applications that interoperate via "remote procedure calls" (RPC) over "stateless" HTTP via HTTP POST. Applications are not limited to RPC via HTTP POST. Other REST methods can be used to manage the state of resources via URL references, allowing the storing of data (HTTP PUT), retrieval of data (HTTP GET), and removal of data (HTTP DELETE) from a resource.

XML-RPC is a generic, self-describing (and very verbose) XML format to compose XML messages for platform-neutral data exchange. XML-RPC defines a collection of frequently used XML types with common programming language equivalents. XML-RPC does NOT provide a data binding to XML and does NOT support a validation mechanism to ensure that data content is validated against a schema. XML-RPC serialization proceeds by marshaling parameters in predefined XML elements for each data type. XML-RPC has primitive types (bool, int, double, string, dateTime, base64) and two compound types (structs and arrays).

This document does not describe XML-RPC in detail. For more details, please visit http://www.xmlrpc.com.

JSON (JavaScript Object Notation) is an even simpler data format to support platform-neutral data interchange that is highly compatible across programming languages by restricting data representation to a set of five common types: bool, float, string, array, and object. A JSON object is the same as an XML-RPC struct. Only the syntax differs. Both are composed of fieldname-value member pairs (i.e. both are hashmaps) and have no other special properties. (Which is in contrast to XML data as "objects" that are namespace scoped and may include xsi:type information to distinguish derived from base types, and may include id-ref data references, and other properties that make XML more suitable to achieve lossless C/C++ serialization.)

This document does not describe JSON (and JSON RPC/REST) in detail. For more details, please visit http://www.json.org.

JSON/JSONPath and gSOAP

The gSOAP C++ JSON API is compact and lightweight. It is straightforward to write JSON RPC and JSON REST code:

#include "json.h"
soap *ctx = soap_new1(SOAP_C_UTFSTRING); // set up context to manage memory
value request(ctx), response(ctx);
request = "getCurrentTime"; // request current time
if (!json_call(ctx, // make a call (POST)
"http://www.cs.fsu.edu/~engelen/currentTimeJSON.cgi", // endpoint URL
request, // value with the request string
response) // get response, if call is OK
)
cout << "Current time = " << response << endl; // JSON response to cout

To help you quickly develop C/C++ JSON code, we include a code generator jsoncpp with the gSOAP package (version 2.8.26 and up). You can find the jsoncpp tool with the JSON examples in gsoap/samples/xml-rpc-json. The jsoncpp command auto-generates C or C++ code from a JSON fragment. The generated code creates a JSON node graph for this fragment, which can be further tweaked as necessary. For example:

$ cat menu.json
{ "menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}
}
$ ./jsoncpp menu.json
#include "json.h"
{ /* Generated by jsoncpp menu.json */
struct soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
ctx->double_format = "%lG";
value x(ctx);
x["menu"]["id"] = "file";
x["menu"]["value"] = "File";
x["menu"]["popup"]["menuitem"][0]["value"] = "New";
x["menu"]["popup"]["menuitem"][0]["onclick"] = "CreateNewDoc()";
x["menu"]["popup"]["menuitem"][1]["value"] = "Open";
x["menu"]["popup"]["menuitem"][1]["onclick"] = "OpenDoc()";
x["menu"]["popup"]["menuitem"][2]["value"] = "Close";
x["menu"]["popup"]["menuitem"][2]["onclick"] = "CloseDoc()";
std::cout << x << std::endl;
soap_destroy(ctx);
soap_end(ctx);
soap_free(ctx);
}

You can use jsoncpp option -M to narrow the generated code down to the essentials without the initialization and cleanup operations. This makes it more suitable for use within your code base.

You can also use the new jsoncpp option -p (gSOAP 2.8.27 and up) to generate efficient JSONPath query code.

For example, let's write a JSONPath query to display the authors of books in a store. We will read the JSON data from std:cin (option -i) and filter the authors with the query $.store.book[*].author to collect them in an array y of results with option -y. We generate the code from the command line with jsoncpp as follows:

$ ./jsoncpp -i -M -p'$.store.book[*].author' -y
value x(ctx);
std::cin >> x;
// $.store.book[*].author
value y(ctx);
#define QUERY_YIELD(v) y[y.size()] = v
if (x.has("store"))
{
if (x["store"].has("book"))
{
value::iterator j = x["store"]["book"].begin();
value::iterator k = x["store"]["book"].end();
for (value::iterator i = j; i != k; ++i)
{
if ((*i).has("author"))
{
QUERY_YIELD((*i)["author"]);
}
}
}
}

The jsoncpp code generator aims to produce clean, high-quality and readable C and C++ code. You can also embed C/C++ code in JSONPath queries to filter and select values based on your runtime data.

We will present in detail how to use jsoncpp in the next section. The remainder of this document explains how you can use the XML-RPC/JSON C and C++ APIs create data, access data, send/recv data via REST, read/write data to files, streams, and string buffers.

It should be stated that JSON as a data format is not a true-and-tested alternative to XML and XML schema. XML data bindings provide a strongly typed interface to exchange validated data with RPC and REST. However, XML can be more complex to provide strong guarantees for object polymorphism (base and derived classes), to accurately represent tree and graph structures, to include binary content natively with base64 (and mechanisms for streaming MIME/MTOM attachments), to support extensibility (to extend data types and to add new data types), and schema namespaces referenced by XML elements and attributes to avoid ambiguity.

The jsoncpp command-line tool

The jsoncpp command produces high-quality readable source code. The generated code can be readily used in your projects to populate JSON data and extract data with compiled JSONPath queries, thereby saving you substantial time and effort to write code. You may not have to write any C or C++ code to manipulate JSON data with your application's code base by taking full advantage of this tool.

The jsoncpp command-line tool generates C or C++ source code to populate a JSON node graph with the data given in a JSON file. The command also has an option -p to generate efficient source code for JSONPath queries. Even stand-alone JSONPath query filter applications can be auto-generated.

Compiling the jsoncpp command

You will find jsoncpp and the XML-RPC/JSON examples in the gSOAP package in gsoap/samples/xml-rpc-json.

To build jsoncpp, install gSOAP and build all sample codes as follows:

./configure --enable-samples
make
make install

This builds the command-line tool jsoncpp in gsoap/samples/xml-rpc-json from where you can use it and/or copy it for use with your projects.

If you do not have the samples built, you can use soapcpp2 (or soapcpp2.exe in gsoap/bin/win32) from the command line to generate the C++ code required by jsoncpp and also required by the C++ JSON API components:

cd gsoap/samples/xml-rpc-json
soapcpp2 -CSL xml-rpc.h
c++ -I../.. -o jsoncpp jsoncpp.cpp json.cpp xml-rpc.cpp soapC.cpp ../../stdsoap2.cpp

The above builds the jsoncpp command-line tool.

Command-line options

The jsoncpp command takes several options and an optional JSON input file:

jsoncpp [-c] [-e] [-f%fmt] [-h] [-i] [-m] [-M] [-O] [-ofile] [-ppath] [-rroot] [-xcode] [-y] [infile]
Option Description
-c generate C code instead of C++
-e add explanatory comments to the generated code
-f%fmtuse %fmt to format double floats, e.g. -f%lg
-h display help message
-i don't read JSON from stdin, generate code that reads JSON instead
-m generate stand-alone code with main()
-M generate minimal code unadorned with initialization and cleanup
-O optimize code by factoring common indices
-ofile save source code to file
-ppath generate JSONPath query code for path
-rroot use root instead of root value x in the generated code
-xcode generate code that executes code for each JSONPath query result
-y generate code that yields an array y of JSONPath query results
infile JSON file to parse

The jsoncpp command expects a JSON input file or it will read JSON data from standard input unless option -i is used. With option -i, the generated source code includes commands to read JSON data from standard input. This is useful to generate code that filters JSON data from input with the JSONPath query given with option -p. Otherwise, the generated code simply builds a node graph in code for the specified JSON input data.

The jsoncpp command emits source code to standard output or to the file specified with option -o.

Minimalist code is generated with option -M, which is useful to automate pasting of the unadorned source code into the source code of your project.

Optimized code is generated with option -O by factoring common array indices and object field names. This produces more elaborate code that is more efficient but may be harder to read and modify.

The default name of the root value in the generated source code is x. To change this name use option -r. Do not use the name v, which represents the current value. Other variable names to avoid are i, j, k, p, q, r, s, and S, since these are internally used by the generated JSONPath query code.

Options -p and -x specify a JSONPath query path and the code to execute for each query result, respectively. The default action in the generated code is to print each query value in JSON format. Option -y yields a JSON array of query values that are incrementally collected. Option -x overrides option -y.

To generate a stand-alone application use option -m. This option is useful for testing JSONPath query filters with option -p, possibly combined with option -i to let the JSONPath filter application read from standard input.

Option -ffmt sets the floating point double precision format to use in the generated code. By default, jsoncpp emits floating point numbers with up to 17 digit mantissas to preserve precision. Use -flG for the smallest floating point representation.

Use option -c to generate C code instead of C++ and add explanatory comments to the generated code using option -e.

JSONPath syntax

We use the JSONPath syntax of Goessner extended with ? ("where") and ! ("where not") operators. We also support the [?(expr)] and [(expr)] constructs to insert your own C/C++ expressions for filtering and selection of nodes in your JSONPath queries.

JSON data structures are represented internally as a node graph consisting of atomic values (null, bool, int/double, string), arrays, and "objects" that are structs with fieldname-value pairs. A JSONPath expression specifies a JSON data query, typically starting from the root node, and descending deeper into the node graph to match child nodes.

For example, suppose we have a store object with a book array. Each book object has a title string (and some other properties we will ignore for now). The following JSONPath query returns the titles of all books in the store:

$.store.book[*].title

We can also write the same query in bracket notation:

$["store"]["book"][*]["title"]

Note that the syntax of this query has a close similarity to the C++ JSON API for accessing field names and array elements.

Basically, a JSONPath expression is a sequence of operations to match nodes:

Operator Nodes matched and returned
$ the root node of the node graph
.f or [f] child node at field named f of the current object node
[n] nth node of the current array node, if indexed within bounds
[b:e:s] array slice of the current array node
[x,y] child nodes matching x or y (fields, indices and slices)
* "wildcard": any child node of the current object/array node
.. "recurse": any matching descendant nodes of the current node
? "where": current node if the rest of the query path matches
! "where not": the complement of ?
[(e)] evaluate C/C++ expression e to match a field or an index
[?(e)] evaluate C/C++ expression e, continue matching when true

Field names (f in the table) in JSON and in JSONPath queries may contain UTF-8 Unicode characters.

Other JSONPath implementations require quotes for field names in brackets, as in `['store']or["store"]`. In this implementation you will only need to add quotes when field names contain control characters, spaces, or punctuation, such as the field name 'unit-price'. To promote orthogonality of the JSONPath syntax (no arbitrary rules and exceptions), quoted field names are also valid in dot notation in this JSONPath implementation.

JSONPath by example

A JSONPath query expression uses dot or bracket operators to match JSON data located at increasingly deeper levels of the data structure.

Consider the following JSON data:

{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}

To match the title of the first book (book[0]) in a store, starting at the root node indicated by $, we use the following JSONPath query expression:

$.store.book[0].title

This query returns "Sayings of the Century" when applied to the JSON data.

To try the JSONPath examples yourself, we suggest to create a store.json file with the above JSON data and run jsoncpp from the command line to compile a JSONPath query as follows:

./jsoncpp -i -o test-json.cpp -m -p'$.store.book[0].title'
c++ -I../.. -o test-json test-json.cpp json.cpp xml-rpc.cpp soapC.cpp ../../stdsoap2.cpp
./test-json < store.json

The compiled JSONPath query is applied to the store.json data and returns the matching values found. Use jsoncpp option -y to return matches in a JSON array. The soapC.cpp file is generated with the command soapcpp2 -CSL xml-rpc.h, which is done just once for all C++ JSON applications.

To match any field of an object or any array element, we use a wildcard *:

$.store.*.price

This matches and returns the bicycle price 19.95, but not the book prices that are located one level deeper in the array of books, which can be matched with:

$.store.*.*.price

This returns 8.95, 12.99, 8.99, and 22.99.

In the latter case we only get the book prices, because the first * matches book and bicycle and the second * matches the book array and the red and price fields. Only the book prices are returned, because red and price are atomic and have no price child node.

To match and return all prices in the store we use .. called "recursive descent" or simply "recurse", as follows:

$..price

Array elements are matched with brackets [n] where n is an array index. Negative indices can be used to access array elements from the end of an array, where -1 refers to the last element. We can list the array elements to match with [x,y], for example:

$.store.book[0,1,-1].title

This matches and returns the titles of the first two books and the last.

JSONPath queries do not modify the node graph searched. So you do not need to worry about indices that are out of bounds or fields that are not part of an object.

Arrays can also be sliced for matching from a starting index b until (excluding) an ending index e with [b:e], where b and e values are optional. When omitted, the slice runs from the start and/or from the end of the array.

For example

$.store.book[:].title

matches and returns the titles of all books in the store, and

$.store.book[:2].title

matches and returns the first two books (at 0 and 1) in the store.

We can use an optional step s to slice arrays with [b:e:s] and even reverse array element-by-element matching with a negative unit step:

$.store.book[::-1].title

This matches and returns the titles of all books in reverse order.

The following JSONPath queries return the same results, where we used slices and [x,y] to match multiple array entries:

$.store.book[1:3].title
$.store.book[1:-1].title
$.store.book[-3:-1].title
$.store.book[1,2].title
$.store.book[-3,-2].title

Basically, JSONPath array slices in our implementation follow the intuitive Python array slice syntax and meaning. Beware that many other JSONPath implementations do not implement the step parameter consistently or do not support stepping.

Note that [:] is not the same as [*] because [:] only matches arrays.

A conditional JSONPath expression contains a ? ("where") operator. The operator returns the results that match the left side of the ? but only when the right-side matches:

$.store.book[:]?isbn

This matches and returns only books that have an isbn field.

The complement of the ? ("where") operator is ! ("where not"), which returns the results that match the left side of the ! but only when the right-side does not match.

More complex queries can be formulated by embedding C/C++ expressions in the query to filter [?(e)] and select [(e)] nodes. For example:

$.store.book[:][?((double)v["price"] < 10.0)].title

This filters books with prices lower than 10.0 and returns the title of each book found.

Embedded C/C++ expressions can inspect the current JSONPath node value by accessing variable v, as is shown above. Here we used (double)v["price"] to obtain the price of the current node for comparison. The JSONPath root node value is x. Instead of x, you can select another name with jsoncpp option -r.

You can access variables and functions in embedded C/C++ expressions, but do not access or modify i, j, k, p, q, r, s and S, which are internally used by the generated JSONPath query code.

Warning
In this respect we should caution you about using C/C++ expressions that modify node values, since this may affect the query results in unpredictable ways. In fact, v["price"] will add a price to any current node value v that has no "price" field! To make field accesses safe we should first check if the field exists in the current node before we access it:
$.store.book[:][?((v.has("price") ? (double)v["price"] : 9999) < 10.0)].title
Guarding field accesses with has() is the only safe way to combine .. with C/C++ filters, since we may visit all nodes in the graph, for example to find all prices < 10.0:
$..[?((v.has("price") ? (double)v["price"] : 9999) < 10.0)].price

Object fields and array elements can be accessed in a JSONPath query with C/C++ expressions that evaluate to string field names or to integers indices, respectively. For example, we can use the string argv[1] of main() as a field name:

$.store.book[:][(argv[1])]

This assumes that the command-line argument (argv[1]) of the application is a book field name. Otherwise, no results are returned.

After compiling the JSONPath query with

./jsoncpp -i -o test-json.cpp -m -p'$.store.book[:][(argv[1])]'
c++ -I../.. -o test-json test-json.cpp json.cpp xml-rpc.cpp soapC.cpp ../../stdsoap2.cpp

we can obtain the book titles with:

./test-json title < store.json

You can use multiple C/C++ expressions in brackets and combine them with other field and array expressions separated by commas:

$.store.book[:][title,(argv[1])]

This prints the title and the value of the field name given by the command-line argument, if there is a field that matches the given name.

Finally, let's use the value of argv to filter products in the store by a given price:

./jsoncpp -i -m -p'$.store..[?((v.has("price") ? (double)v["price"] : 9999) < strtod(argv[1], NULL))]'

C/C++ expressions cannot be used as array slice bounds, which must be constant.

C++ XML-RPC and JSON

XML-RPC and JSON data is interchangeable in this implementation, with the only exception that the dateTime and base64 types are handled as strings in JSON. Also, JSON's only numeric type is floating point. However, integers are handled just fine by this JSON implementation as 64 bit (long long, int64_t, LONG64) without conversion to/from double floating point values.

List of C++ files

The following files define XML-RPC operations and data types for C++:

For JSON we use the following files for C++:

The gSOAP header file xml-rpc.h defines all XML-RPC and JSON types as struct with C++ member functions to create XML-RPC and JSON data and REST messages.

A note about the following auto-generated files: soapH.h, soapStub.h and soapC.cpp. These are required for XML-RPC and JSON. To auto-generate these files, execute:

soapcpp2 -CSL xml-rpc.h

Then compile and link the .cpp files listed above for XML-RPC and JSON with the auto-generted soapC.cpp and stdsoap2.cpp (or link with libgsoap++.a installed by the gSOAP package).

Because XML namespaces are not used, we can either use -DWITH_NONAMESPACES to compile stdsoap2.cpp without complaining about a missing global Namespace, or we can define an empty namespaces table somewhere in our code:

struct Namespace namespaces[] = {{NULL,NULL,NULL,NULL}};

C++ XML-RPC and JSON with examples

An XML-RPC/JSON data value is created in C++ as follows, which requires a context ctx with the engine state (the soap struct). The context manages the memory that is internally allocated to hold values.

#include "soapH.h"
soap *ctx = soap_new1(SOAP_C_UTFSTRING); // new context
value v(ctx);
soap_destroy(ctx); // delete all values
soap_end(ctx); // delete temp data
soap_free(ctx); // free context

Note that soapH.h is an auto-generated file (see previous section). This file needs to be generated only once and for all. It also references stdsoap2.h and the auto-generated soapStub.h. Compile and link your code with stdsoap2.cpp and the auto-generated soapC.cpp XML-RPC serializers. Also compile and link xml-rpc.cpp. For JSON, compile and link json.cpp.

We can stack-allocate local values as shown above. To allocate a value on the heap that is managed by the engine context, use new_value(ctx):

value *v = new_value(ctx);
...
soap_destroy(ctx); // delete all values
soap_end(ctx); // delete temp data

You can use wide strings with Unicode stored in UTF-8-formattted 8-bit char strings. For compatibility with XML-RPC serialization of UTF-8-encoded strings, we MUST use the SOAP_C_UTFSTRING flag to initialize the context with soap_new1(SOAP_C_UTFSTRING). We can optionally use SOAP_XML_INDENT to indent XML and JSON.

The code shown above creates an empty value v. Values can be assigned any one of the following data types:

v = 12345LL; // 64 bit int
v = 12.34; // double float
v = "abc"; // string
v = string("abc"); // std::string
v = L"xyz"; // wide string (converted to UTF-8)
v = wstring(L"xyz"); // std::wstring (converted to UTF-8)
v = false; // Boolean
v = time(0); // time_t value serialized as ISO 8601 date time
// create an array [24, 99.99, "abc"]
v[0] = 24;
v[1] = 99.99;
v[2] = "abc";
// create a struct (JSON object) {"name": "gsoap", "major": 2.8, "©": 2015}
v["name"] = "gsoap";
v["major"] = 2.8;
v[L"©"] = 2015; // wide string tags are OK
// create a base64-encoded image object
_base64 img(ctx, 100, ptr_to_rawimage100bytes); // block of 100 raw bytes
v = img;

We can combine this syntax in many possible ways to create arrays of arrays, arrays of structs, and so on. For example:

v[0]["name"] = "bob";
v[0]["toys"][0] = "ball";
v[0]["toys"][1] = "furby";

This creates a singleton array containing an object with two members: name set to "bob" and toys set to an array containing "ball" and "furby". In JSON format this is represented as:

           [ { "name": "bob", "toys": ["ball", "furby"] } ]
           ^ ^ ^              ^
           | | |              |
  an array_/ | |              |
 of 1 struct_/ |              |
with 2 members_/______________/

When receiving a value in XML-RPC or JSON, we generally want to check its type to obtain its value. To check the type of a value, we use is_Type methods:

bool value::is_null() // true if value is not set or assigned (JSON null)
bool value::is_int() // true if value is a 32 or a 64 bit int
bool value::is_double() // true if value is a 64 bit double floating point
bool value::is_string() // true if value is a string or wide string
bool value::is_bool() // true if value is a Boolean "true" or "false" value
bool value::is_true() // true if value is Boolean "true"
bool value::is_false() // true if value is Boolean "false"
bool value::is_array() // true if array of values
bool value::is_struct() // true if structure, a.k.a. a JSON object
bool value::is_dateTime() // true if ISO 8601, always false for received JSON
bool value::is_base64() // true if base64, always false for received JSON

The following methods can be used to inspect arrays and structs (JSON objects):

void value::size(int) // reset array size or pre-allocate space
int value::size() // returns array or struct size or 0
bool value::empty() // true if array or struct is empty
bool value::has(int) // true if index is within array bounds
bool value::has(const char*) // true if struct has field
bool value::has(const wchar_t*) // true if struct has field
int value::nth(int) // returns index >= 0 if index is in array bounds, < 0 otherwise
int value::nth(const char*) // returns index >= 0 of field in struct, < 0 otherwise
int value::nth(const wchar_t*) // returns index >= 0 of field in struct, < 0 otherwise
value& value::operator[int] // returns value at index in array or struct
value& value::operator[const char*] // returns value at field in struct
value& value::operator[const wchar_t*] // returns value at field in struct

For example, let's take the value v that was assigned the array shown above. We have the following properties of this value:

v.is_null() == false // v is not null
v.is_array() == true // v is an array
v.size() == 1 // v has one element
v.has(1) == false // v has no array element at index 1
v.nth(-1) == 0 // v last element is at index 0
v[0].is_struct() == true // v[0] is a struct
v[0].has("name") == true // v[0] has field name "name"
v[0].nth("name") == 0 // v[0] has field name "name" at index 0
v[0][0].is_string() == true // v[0][0] == v[0]["name"] is a string
v[0].has("toys") == true // v[0] has field name "toys"
v[0]["toys"].is_array() == true // v[0]["toys"] is an array
v[0]["toys"].empty() == false // v[0]["toys"] is not empty

When accessing structs (JSON objects) with field names, make sure to use existing member field names only. A new member fieldname-value pair is dynamically added to the structure to accomodate the new entry for the field.

Also arrays are extended to accommodate the indexed array element. A negative index accesses elements from the array's end, with index -1 accessing the last value. Also the has and nth methods take a negative index for bounds checking on arrays and will return false or negative, respectively.

You may want to use iterators to extract data from structs and arrays (see further below).

To extract atomic data we can use casts on a value v as follows:

(double)v // 64 bit double or 0.0 if not numeric
(int)v // 32 bit int or 0 if not numeric
(LONG64)v // 64 bit int or 0 if not numeric
(char*)v // convert to string
(string)v // convert to std::string
(wchar_t*)v // convert to wide string
(wstring)v // convert to std::wstring
(bool)v // same as is_true()
(time_t)v // nonzero if v contains an ISO 8601 date time
(_base64)v // base64 encoding of v

It is valid to cast a primitive type to any of the other primitive types shown above and no runtime error will occur, although you may loose some information when an atomic value has no representation in the target type's value space. Casting a number to a string is fine, but casting a string to a number only gives a nonzero numeric value if the string is numeric. Casting a value to base64 produces its base64 encoding.

To access base64 binary raw data of a value v, we use the following methods:

_base64& base64 = v;
unsigned char *raw = base64.ptr(); // points to raw binary data
int size = base64.size(); // that is of this size

Arrays and structs are compound types that cannot be cast to or from other types (but if you do cast, an empty array or struct is created an no runtime error will occur). So we should check for array and struct types to obtain their content. For example:

if (v.is_array())
{
for (int i = 0; i < v.size(); ++i)
{
value& array_value = v[i];
... // use and/or set array_value
}
}

We use the iterators value::iterator and value::const_iterator to loop over the values in structs and arrays:

if (v.is_struct() || v.is_array())
{
for (value::iterator i = v.begin(); i != v.end(); ++i)
{
int index = i.index(); // index of element
const char *name = i.name(); // name() is nonempty for structs
value& element = *i;
... // use index, name, and/or use/set the element value
}
}

The iterator method value::iterator::index() returns the integer index of the struct or array element. The value::iterator::name() method returns the name of the struct member, or empty "" if the type is not a struct. The value::const_iterator does not permit the value referenced by the iterator to be modified.

There are two lower level iterators for structs and arrays, which are slightly more efficient to use compared to the value::iterator. These array and struct iterators have an index() method to obtain the index (an int). Struct iterators have a name() method to obtain a member's name (a string).

For example, to traverse a value v that is an array or a struct:

if (v.is_array())
{
_array& vec = v; // cast to _array
for (_array::iterator i = vec.begin(); i != vec.end(); ++i)
{
int array_index = i.index();
value& array_value = *i;
... // use array_index, use and/or set array_value
}
}
else if (v.is_struct())
{
_struct& rec = v; // cast to _struct
for (_struct::iterator i = rec.begin(); i != rec.end(); ++i)
{
const char *member_name = i.name();
value& member_value = *i;
... // use member_name, use and/or set member_value
}
}

As usual, the _struct::const_iterator and _array::const_iterator do not permit the values referenced by the iterator to be modified.

XML-RPC parameter lists are similar to arrays and its values are indexed. We can also iterate over response parameters after an XML-RPC REST call:

methodCall rpc(ctx, "endpoint URL", "methodName");
params request(ctx, 2);
request[0] = ...; // first request parameter
request[1] = ...; // second request parameter
params response = rpc(request); // execute the call
if (!rpc.error())
{
for (params::iterator i = response.begin(); i != response.end(); ++i)
{
int index = i.index();
value& param_value = *i;
... // use param_value of response params
}
}

We should note that JSON REST does not require parameter types, for example:

#include "json.h"
value request(ctx);
value response(ctx);
request[0] = ...; // first request parameter
request[1] = ...; // second request parameter
if (!json_call(cts, "endpoint URL", request, response))
{
... // use response value
}

There are two additional methods to invoke on parameters:

int params::size(); // returns number of parameters
bool params::empty(); // true if no parameters

All dynamically allocated memory that is internally used to store data is deallocated with:

soap_destroy(ctx); // delete all values
soap_end(ctx); // delete temp data
soap_free(ctx); // delete context allocated with soap_new()

Additional examples are located in gsoap/samples/xml-rpc-json:

C++ XML-RPC client example

A typical XML-RPC calling sequence in C++ is:

#include "soapH.h" // generated by the command: soapcpp2 -CSL xml-rpc.h
#include "xml-rpc-io.h" // to serialize XML-RPC data to streams
struct Namespace namespaces[] = {{NULL,NULL,NULL,NULL}}; // no XML namespaces
// create a context
soap *ctx = soap_new1(SOAP_C_UTFSTRING); // store Unicode in UTF-8 format
ctx->send_timeout = 10; // 10 sec, stop if server is not accepting msg
ctx->recv_timeout = 10; // 10 sec, stop if server does not respond in time
// create an XML-RPC method call object
methodCall rpc(ctx, "endpoint URL", "methodName");
// populate the parameters
rpc[0] = 123; // first parameter is an integer
rpc[1] = "abc"; // second is a string
rpc[2]["name"] = "joe"; // a record, first member "name"
rpc[2]["age"] = 23; // a record, second member "age"
rpc[3][0] = 456.789; // an array, first element (a float)
rpc[3][1] = "widget"; // an array, second element (a string)
rpc[3][2] = true; // an array, third element (a bool)
// make the XML-RPC call and retrieve response
params response = rpc(request);
// check result
if (rpc.error())
soap_stream_fault(ctx, std::err);
else if (response.empty())
std::cout << rpc.fault() << std::endl;
else if (response.size() > 1)
std::cout << "More than one response data" << std::endl;
else if (response[0].is_array() && !response[0].empty())
for (int i = 0; i < response[0].size(); i++)
... = response[0][i];
else if (response[0].is_struct())
{
... = response[0]["membername1"];
... = response[0]["membername2"];
}
else if (response[0].is_base64())
{
_base64 base64& = response[0];
unsigned char *raw = base64.ptr();
int size = base64.size();
... // use raw[0..size-1] data
}
else if (response[0].is_bool())
{
bool flag = response[0];
... // use boolean flag
}
else if (response[0].is_int())
{
LONG64 num = response[0];
... // use integer
}
else if (response[0].is_double())
{
double num = response[0];
... // use double float
}
else if (response[0].is_string())
{
const char *str = response[0];
// use string, note that also legal is:
const std::string& st = response[0];
// and conversion from UTF-8 to wide string unicode:
const wchar_t *w = response[0];
const std::string& ws = response[0];
}
else if (response[0].is_dateTime())
{
time_t t = response[0];
... // use time
}
// deallocate all
soap_destroy(ctx);
soap_end(ctx);
soap_free(ctx);

Alternatively, parameters of a methodCall can be passed with the methodCall itself as follows:

methodCall rpc(ctx, "endpoint URL", "methodName");
// create 5 parameters
params request(ctx, 5);
// populate the parameters
request[0] = 123; // first parameter is an integer
request[1] = "abc"; // second is a string
request[2]["name"] = "joe"; // a record, first member "name"
request[2]["age"] = 23; // a record, second member "age"
request[3][0] = 456.789; // an array, first element (a float)
request[3][1] = "widget"; // an array, second element (a string)
request[3][2] = true; // an array, third element (a bool)
// make the XML-RPC call with request parameters, retrieve response
params response = rpc(request);

Note that in the client code, after the response is retrieved, the implicit type casts done by assignments extract the values. These casts can be used anywhere to extract values:

params response = rpc();
double sum = 0.0;
for (int i = 0; i < response.size(); i++)
if (response[i].is_double()) // is this parameter a double float?
sum += (double)response[i];

Type casts can also be used to convert data, which means they never produce an exception. Casting to string (const char*) converts atomic values to strings, but does not convert compound types such as arrays and structs.

params response = rpc();
for (int i = 0; i < response.size(); i++)
printf("response[%d] = %s\n", i, (const char*)response[i]);

which prints a string representation of the int, double, boolean, time, or base64 values of parameters. An empty string is printed for arrays and structs. Use iterators to walk over arrays and structs to print values. Or use the JSON API json.h and json.cpp to print values in JSON format, see further on JSON below.

C++ XML-RPC server example

A typical C++ XML-RPC server sequence is:

// create an allocation context
soap *ctx = soap_new1(SOAP_C_UTFSTRING);
// create an XML-RPC methodCall object
methodCall rpc(ctx);
// Option 1: parse and write to/from stdin/out for CGI
// (no code needed)
// Option 2: parse and write to/from FILE or socket
// ctx->recvfd = ...; // set input FD
// ctx->sendfd = ...; // set output FD
// Option 3: parse and write to/from IO streams
// ctx->is = ...; // set input stream
// ctx->os = ...; // set output stream
if (rpc.recv() != SOAP_OK)
soap_print_fault(ctx, stderr);
else
{
// create response
methodResponse response(ctx);
// check method name
if (!strcmp(rpc.name(), "methodName"))
{
// method name matches: populate response parameters with values:
response[0] = ...;
response[1] = ...;
... // add response data
}
else
{
// otherwise, set fault
response.set_fault("Wrong method");
}
// send response
if (response.send() != SOAP_OK)
soap_print_fault(ctx, stderr);
}
// close (but keep-alive setting keeps socket open)
soap_closesock(ctx);
// clean up
soap_destroy(ctx);
soap_end(ctx);
soap_free(ctx);

With option 1 the server code above uses standard in/out and thus runs over CGI. Other possibilities are given by options 2 and 3.

To serve requests at a port, we use the soap_bind() and soap_accept() calls to bind the server to a port and accept requests via socket, see also the docs and examples for these calls (see for example gsoap/samples/webserver.c):

// create an allocation context
soap *ctx = soap_new1(SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING);
// bind to port 8080
if (!soap_valid_socket(soap_bind(ctx, NULL, 8080, 100)))
... // error, stop
// accept messages in server loop
for (;;)
{
if (!soap_valid_socket(soap_accept(ctx)))
... // error, stop
// create a method object
methodCall rpc(ctx);
// parse it from socket
if (rpc.recv() != SOAP_OK)
soap_print_fault(ctx, stderr);
... // process request, produce result to send as shown above
// close (but keep-alive setting keeps socket open)
soap_closesock(ctx);
// clean up
soap_destroy(ctx);
soap_end(ctx);
}
// free context
soap_free(ctx);

C++ XML-RPC serialization from/to streams

To send and receive XML over streams, use xml-rpc-io.h and xml-rpc-io.cpp. For example:

#include "xml-rpc-io.h"
std::cout << response[0] << std::endl;

which will display the data in XML-RPC format. To parse XML-RPC data from a stream, use:

#include "xml-rpc-io.h"
value v(ctx);
std::cin >> v;

Compile and link together with soapC.cpp, xml-rpc.cpp, xml-rpc-io.cpp, and stdsoap2.cpp.

C++ JSON serialization from/to streams

To display values in JSON format or to parse JSON data, use the json.h and json.cpp JSON serializers in combination with xml-rpc.cpp and the auto-generated soapH.h and soapC.cpp. It is also possible to send and receive JSON data over HTTP as JSON REST operations, but this requires some more coding (see JSON over HTTP below).

Because the internal data is the same for XML-RPC and JSON, You can write data in XML-RPC or in JSON format. You can also parse XML-RPC data and write to JSON data and vice versa.

For example, you can parse a JSON-formatted string and use that data to make an XML-RPC call. The result of the call is displayed in JSON, nicely indented using the SOAP_XML_INDENT flag (this XML indent flag also works for JSON):

#include "json.h"
#include <sstream>
struct Namespace namespaces[] = {{NULL,NULL,NULL,NULL}}; // no XML namespaces
// SOAP_C_UTFSTRING: UTF-8 content in char*, SOAP_XML_INDENT: indent JSON
soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
value request(ctx);
istringstream in;
in.str("[ [1, \"2\", 3.14, true], {\"name\": \"john\", \"age\": 24} ]");
in >> request; // parse JSON, store as XML-PRC data
params response = rpc(request); // make the XML-RPC call
std::cout << response << std::endl; // display result in JSON (indented)

Compile and link together with soapC.cpp, xml-rpc.cpp, json.cpp, and stdsoap2.cpp.

The JSON protocol has fewer data types than XML-RPC, so type information can be lost when serializing to JSON:

See the section on C++ examples on how to populate and extract C++ data.

Strings are stored and exchanged in UTF-8 format in 8-bit strings (char* and std::string) by using the SOAP_C_UTFSTRING flag. Without this flag, 8-bit strings are converted to UTF-8. We can optionally use SOAP_XML_INDENT to indent XML and JSON.

To force reading and writing JSON in ISO 8859-1 format, use the SOAP_ENC_LATIN flag to set the context.

C++ JSON over HTTP (REST method)

To use JSON REST on the client side, we use json_call:

#include "json.h" // also compile and link json.cpp
struct Namespace namespaces[] = {{NULL,NULL,NULL,NULL}}; // no XML namespaces
// SOAP_C_UTFSTRING: UTF-8 content in char*, SOAP_XML_INDENT: indent JSON
soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
value request(ctx), response(ctx);
... // now populate the request data to send
if (json_call(ctx, "URL", &request, &response) != SOAP_OK)) // POST
... // error
else
... // use the response data
... // make other calls etc.
soap_destroy(ctx); // delete all values
soap_end(ctx);
... // make other calls etc.
soap_free(ctx); // free context

The json_call function takes a context, an endpoint URL (with query string parameters as needed), and optional in and out values to send and receive, respectively. The function returns SOAP_OK (zero) for success or EOF, SOAP_SYNTAX_ERROR, or an HTTP error code.

To use the JSON REST POST method, pass both in and out values to json_call. For the GET method, pass a NULL to in. For the PUT method, pass a NULL to out. For the DELETE method, pass both NULL to in and out.

Besides json_call, there are other JSON API functions:

The are two other lower-level functions json_send and json_recv that are similar to json_write and json_read but do not initialize the sending and receiving operations and do not flush after the sending and receiving operations.

To implement a JSON REST server for CGI (e.g. install in cgi-bin):

#include "json.h" // also compile and link json.cpp
struct Namespace namespaces[] = {{NULL,NULL,NULL,NULL}}; // no XML namespaces
int main()
{
soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
value request(ctx), response(ctx);
if (soap_begin_recv(ctx)
|| json_recv(ctx, request)
|| soap_end_recv(ctx))
soap_send_fault(ctx);
else
{
... // use the 'request' value
... // set the 'response' value
// set http content type
ctx->http_content = "application/json; charset=utf-8";
// send http header and body
if (soap_response(ctx, SOAP_FILE)
|| json_send(ctx, response)
|| soap_end_send(ctx))
soap_print_fault(ctx, stdout);
}
// dealloc all
soap_destroy(ctx);
soap_end(ctx);
soap_free(ctx);
}

Compile and link the code together with soapC.cpp, xml-rpc.cpp, json.cpp, and stdsoap2.cpp.

For client and server examples, please see the gSOAP package content:

Moving JSON types into a C++ namespace

A C++ namespace is preferred to separate JSON types and operations from other project-related types and operations.

To put all JSON (and XML-RPC) types and operations in a json C++ namespace, execute the following commands:

soapcpp2 -qjson -CSL xml-rpc.h
soapcpp2 -penv -CSL env.h

where env.h is an empty file. This generates jsonStub.h, jsonH.h, jsonC.cpp, and envC.cpp.

Then compile the source files together with xml-rpc.cpp and json.cpp and set the macro -DJSON_NAMESPACE:

c++ -DJSON_NAMESPACE xml-rpc.cpp json.cpp jsonC.cpp envC.cpp stdsoap2.cpp ...

Your project should now use the json namespace with a value, for example:

#include "json.h"
soap *ctx = soap_new1(SOAP_C_UTFSTRING);
json::value v(ctx);
std::cin >> v; // parse JSON
std::cout << v; // output JSON

C XML-RPC and JSON

With the release of gSOAP 2.8.26, the XML-RPC and JSON C APIs have been greatly improved. The material in this section pertains to gSOAP 2.8.26 and later.

The new C API for XML-RPC and JSON makes it much easier to populate and extract data, but not as simple and easy as the C++ API.

List of C files

The following files define XML-RPC operations and data types:

In addition to the files above, for JSON we also need the following files:

The gSOAP header file xml-rpc.h defines all XML-RPC and JSON types and the C API functions to create XML-RPC and JSON data for REST messages.

A note about the following auto-generated files: soapH.h, soapStub.h and soapC.c: these are required for XML-RPC and JSON. To auto-generate these files, execute:

soapcpp2 -c -CSL xml-rpc.h

Then compile and link the .c files listed above for XML-RPC and JSON with the auto-generted soapC.c and stdsoap2.c (or link with libgsoap.a installed by the gSOAP package).

Because XML namespaces are not used, we can either use -DWITH_NONAMESPACES to compile stdsoap2.c without complaining about a missing global Namespace, or we can define an empty namespaces table somewhere in our code:

struct Namespace namespaces[] = {{NULL,NULL,NULL,NULL}};

C XML-RPC and JSON with examples

An XML-RPC/JSON data value is created in C as follows, which requires a context ctx with the engine state (the soap struct). The context manages the memory that is internally allocated to hold values.

#include "soapH.h"
struct soap *ctx = soap_new1(SOAP_C_UTFSTRING); /* new context */
struct value *v = new_value(ctx);
soap_end(ctx); /* delete all values */
soap_free(ctx); /* free context */

Note that soapH.h is an auto-generated file (see previous section). This file needs to be generated only once and for all. It also references stdsoap2.h and the auto-generated soapStub.h. Compile and link your code with stdsoap2.cpp and the auto-generated soapC.cpp XML-RPC serializers. Also compile and link xml-rpc.cpp. For JSON, compile and link json.cpp.

You can use wide strings with Unicode stored in UTF-8-formattted 8-bit char strings. For compatibility with XML-RPC serialization of UTF-8-encoded strings, we MUST use the SOAP_C_UTFSTRING flag to initialize the context with soap_new1(SOAP_C_UTFSTRING).

The code shown above creates an empty value v. Values can be assigned any one of the following data types:

*int_of(v) = 12345LL; /* 64 bit int */
*double_of(v) = 12.34; /* double float */
*string_of(v) = "abc"; /* string */
*string_of(v) = soap_wchar2s(ctx, L"xyz");
/* wide string (converted to UTF-8) */
*bool_of(v) = 0; /* Boolean false (0) or true (1) */
*dateTime_of(v) = soap_dateTime2s(ctx, time(0));
/* time_t value serialized as ISO 8601 date time */
/* create an array [24, 99.99, "abc"] */
*int_of(nth_value(v, 0)) = 24;
*double_of(nth_value(v, 1)) = 99.99;
*string_of(nth_value(v, 2)) = "abc";
/* create a struct (JSON object) {"name": "gsoap", "major": 2.8, "©": 2015} */
*string_of(value_at(v, "name")) = "gsoap";
*double_of(value_at(v, "major")) = 2.8;
*int_of(value_atw(v, L"©")) = 2015; /* wide string tags are OK */
/* create a base64-encoded image object */
struct _base64 *img = base64_of(v);
img->__ptr = ptr_to_rawimage100bytes; /* block of 100 raw bytes */
img->__size = 100;

The functions above return a pointer to a specific type of value and this value can be assigned as shown above but also read. So we use these functions also to extract data, for example after receiving XML-RPC or JSON data.

We can combine this syntax in many possible ways to create arrays of arrays, arrays of structs, and so on. For example:

*string_of(value_at(nth_value(v, 0), "name")) = "bob";
*string_of(nth_value(value_at(nth_value(v, 0), "toys"), 0)) = "ball";
*string_of(nth_value(value_at(nth_value(v, 0), "toys"), 1)) = "furby";

This creates a singleton array containing an object with two members: name set to "bob" and toys set to an array containing "ball" and "furby". In JSON format this is represented as:

           [ { "name": "bob", "toys": ["ball", "furby"] } ]
           ^ ^ ^              ^
           | | |              |
  an array_/ | |              |
 of 1 struct_/ |              |
with 2 members_/______________/

When receiving a value in XML-RPC or JSON, we generally want to check its type to obtain its value. To check the type of a value, we use is_Type functions:

is_null(v) /* true if value is not set or assigned (JSON null) */
is_int(v) /* true if value is a 32 or a 64 bit int */
is_double(v) /* true if value is a 64 bit double floating point */
is_string(v) /* true if value is a string */
is_bool(v) /* true if value is a Boolean "true" or "false" value */
is_true(v) /* true if value is Boolean "true" */
is_false(v) /* true if value is Boolean "false" */
is_array(v) /* true if array of values */
is_struct(v) /* true if structure, a.k.a. a JSON object */
is_dateTime(v) /* true if ISO 8601, always false for received JSON */
is_base64(v) /* true if base64, always false for received JSON */

The following functions can be used with arrays and structs (JSON objects):

int has_size(v) /* returns array or struct size or 0 */
struct value *nth_value(v, int) /* returns nth value in array or struct */
struct value *value_at(v, const char*) /* returns value at field in struct */
struct value *value_atw(v, const wchar_t*) /* returns value at field in struct */
int nth_at(v, const char*) /* returns nth index of field in struct or -1
int nth_atw(v, const wchar_t*) /* returns nth index of field in struct or -1

When accessing structs (JSON objects) with value_at, make sure to use existing member field names only. A new member fieldname-value pair is dynamically added to the structure to accomodate the new entry for the field.

Also arrays are extended with nth_value to accommodate the indexed array element.

A negative array index indexes elements from the end of the array, with index -1 accessing the array's last value.

For example, let's take the value v that was assigned the array shown above. We have the following properties of this value:

is_null(v) == false
is_array(v) == true
has_size(v) == 1
is_struct(nth_value(v, 0)) == true
nth_at(nth_value(v, 0), "name") == 0
is_string(value_at(nth_value(v, 0), "name")) == true
is_string(nth_value(value_at(nth_value(v, 0), "toys"), 0)) == true
is_string(nth_value(value_at(nth_value(v, 0), "toys"), 1)) == true

Considering that the code verbosity quickly increases when accessing deeper levels of your structures, you are probably inclined to define your own macros to create and access deep data more conveniently, such as:

#define string_at(v, s) string_of(value_at((v), (s)))
#define nth_string(v, n) string_of(nth_value((v), (n)))
#define nth_string_at(v, s, n) string_of(nth_value(value_at((v), (s)), (n)))
#define string_at_nth(v, n, s) string_of(value_at(nth_value((v), (n)), (s)))
#define nth_string_at_nth(v, n, s, m) string_of(nth_value(value_at(nth_value((v), (n)), (s)), (m)))
... etc ...
*string_at_nth(v, 0, "name") = "bob";
*nth_string_at_nth(v, 0, "toys", 0) = "ball";
*nth_string_at_nth(v, 0, "toys", 1) = "furby";

To iterate over array and struct values, we use a loop over nth_value and nth_member as follows:

if (is_array(v))
{
int i;
for (i = 0; i < has_size(v); i++)
{
struct value *array_value = nth_value(v, i);
... /* use and/or set array_value */
}
}
else if (is_struct(v))
{
int i;
for (i = 0; i < has_size(v); i++)
{
struct member *member = nth_member(v, i);
const char *member_name = member->name;
struct value *member_value = &member->value;
... /* use member_name and member_value */
}
}

To access base64 binary raw data of a value v, we use the following code:

struct _base64 *base64 = base64_of(v);
unsigned char *raw = base64->__ptr; /* point to raw binary data */
int size = base64->__size; /* that is of this size */

XML-RPC parameter lists are similar to arrays and its values are indexed. We can also loop over response parameters after an XML-RPC REST call:

struct params *request = new_params(ctx);
struct methodResponse response;
*string_of(nth_param(request, 0)) = "hello";
*string_of(nth_param(request, 1)) = "world";
if (call_method(ctx, "endpoint URL", "methodName", request, &response) == SOAP_OK)
{
int i;
for (i = 0; i < response.params->__size; i++)
{
struct value *param_value = nth_param(response.params, i);
... /* use param_value */
}
}

We should note that JSON REST does not require parameter types, for example:

#include "json.h"
struct value *request = new_value(ctx);
struct value *response = new_value(ctx);
*string_of(nth_value(request, 0)) = "hello";
*string_of(nth_value(request, 1)) = "world";
if (json_call(cts, "endpoint URL", request, response) == SOAP_OK)
{
... /* use response value */
}

All dynamically allocated memory that is internally used to store data is deallocated with:

soap_end(ctx); /* delete all values */
soap_free(ctx); /* delete context allocated with soap_new() */

Additional examples are located in gsoap/samples/xml-rpc-json:

C XML-RPC client example

An XML-RPC method call in C with the new XML-RPC C API:

#include "soapH.h" /* generated by the command: soapcpp2 -CSL xml-rpc.h */
struct soap *ctx = soap_new1(SOAP_C_UTFSTRING); /* UTF-8 in char* strings */
struct params *request = new_params(ctx);
struct methodResponse response;
ctx->send_timeout = 10; /* 10 sec, stop if server is not accepting msg */
ctx->recv_timeout = 10; /* 10 sec, stop if server does not respond in time */
/* first parameter is an integer */
*int_of(nth_param(request, 0)) = 123;
/* second parameter is a string */
*string_of(nth_param(request, 1)) = "abc";
/* third parameter is a struct {"name": "joe", "age": 23} */
*string_of(value_at(nth_param(request, 2), "name")) = "joe";
*int_of(value_at(nth_param(request, 2), "age")) = 23;
/* fourth parameter is an array [456.789, "widget", true] */
*double_of(nth_value(nth_param(request, 3), 0)) = 456.789
*string_of(nth_value(nth_param(request, 3), 1)) = "widget";
*bool_of(nth_value(nth_param(request, 3), 2)) = 1;
/* connect, send request, and receive response */
if (call_method(ctx, "endpoint UTL", "methodName" request, &response))
{
soap_print_fault(ctx, stderr);
}
else if (response.fault)
{
/* write fault to stdout */
soap_write_fault(ctx, response.fault);
}
else
{
/* print response parameters */
int i;
for (i = 0; i < response.params->__size; i++)
{
printf("Return parameter %d = ", i+1);
display(nth_param(response.params, i)); /* see below */
printf("\n");
}
}

The following example shows how to traverse the node graph to display a value:

void display(struct value *v)
{
if (is_bool(v))
printf(is_true(v) ? "true" : "false");
else if (is_int(v))
printf("%lld", int_of(v));
else if (is_double(v))
printf("%lG", double_of(v));
else if (is_string(v))
printf("\"%s\"", string_of(v));
else if (is_array(v))
{
int i;
printf("[");
for (i = 0; i < has_size(v); i++)
{
if (i) printf(",");
display(nth_value(v, i));
}
printf("]");
}
else if (is_struct(v))
{
int i;
printf("{");
for (i = 0; i < has_size(v); i++)
{
if (i) printf(",");
printf("\"%s\": ", nth_member(v, i)->name);
display(&nth_member(v, i)->value);
}
printf("}");
}
else if (is_dateTime(v))
printf("\"%s\"", dateTime_of(v));
else if (is_base64(v))
printf("(%d bytes of raw data at %p)", base64_of(v)->__size, base64_of(v)->__ptr);
}

Compile and link together with soapC.c, xml-rpc.c, and stdsoap2.c.

C JSON serialization

To write values in JSON format or parse JSON data, we use the json.h and json.c JSON C API. It is also possible to send and receive JSON data over HTTP (JSON REST).

You can also convert XML-RPC data to/from JSON and populate XML-RPC from JSON data. For example:

#include "json.h" /* also compile and link json.c */
struct Namespace namespaces[] = {{NULL,NULL,NULL,NULL}}; /* no XML namespaces */
/* SOAP_C_UTFSTRING: UTF-8 content in char*, SOAP_XML_INDENT: indent JSON */
struct soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
struct value *v = new_value(ctx);
ctx->recvfd = ...; /* set file descriptor for reading */
json_read(ctx, v); /* read JSON into value v */
ctx->sendfd = ...; /* set file descriptor for writing */
json_write(ctx, v); /* write value v in JSON format (indented) */

Compile and link together with soapC.c, xml-rpc.c, json.c, and stdsoap2.c.

The JSON protocol has fewer data types than XML-RPC, so type information can be lost when serializing to JSON:

Strings are stored and exchanged in UTF-8 format in 8-bit strings (char*) by using the SOAP_C_UTFSTRING flag. Without this flag, 8-bit strings are converted to UTF-8.

To force reading and writing JSON in ISO 8859-1 format, use the SOAP_ENC_LATIN flag to set the context.

To read JSON from a string buffer, we suggest to use the gSOAP engine's IO frecv callback function as follows:

size_t buf_recv(struct soap *ctx, char *buf, size_t len)
{
const char *in = (char*)ctx->user; /* get handle to input string */
size_t n = strlen(in);
if (n > len) /* if in[] is larger than buf[] len */
n = len; /* then cap length at len */
memcpy(buf, in, n);
in += n;
ctx->user = (void*)in; /* update the handle */
return n;
}
...
value *v = new_value(ctx);
const char *json_in = "[ [1, \"2\", 3.14, true], {\"name\": \"john\", \"age\": 24} ]":
ctx->frecv = buf_recv;
ctx->user = (void*)json_in; /* a user handle that is passed to buf_recv */
soap_read_value(ctx, v);

To write JSON to a string buffer, we suggest to use the gSOAP engine IO fsend callback function as follows:

#define BUFFER_INCREMENT (1024)
int buf_send(struct soap *ctx, const char *buf, size_t len)
{
char *out = (char*)ctx->user; /* get handle to current buffer, if any */
size_t n = out ? strlen(out) : 0;
size_t k = (n + len + 1)/BUFFER_INCREMENT;
if (!out) /* first time around? */
{
out = malloc((k + 1) * BUFFER_INCREMENT);
}
else if (n/BUFFER_INCREMENT < k) /* need to increase buffer? */
{
char *more = malloc((k + 1) * BUFFER_INCREMENT);
memcpy(more, out, n);
free(out);
out = more;
}
memcpy(out + n, buf, len);
out[n + len] = '\0';
ctx->user = (void*)out;
return SOAP_OK;
}
...
value *v = new_value(ctx);
const char *json_out;
... /* populate value v */
ctx->fsend = buf_send;
ctx->user = NULL;
soap_write_value(ctx, v);
json_out = (char*)ctx->user;
if (json_out)
{
... /* use json_out string */
free(json_out);
ctx->user = NULL;
}

C JSON over HTTP (REST method)

To use JSON REST on the client side, we use json_call:

#include "json.h" /* also compile and link json.c */
struct Namespace namespaces[] = {{NULL,NULL,NULL,NULL}}; /* no XML namespaces */
/* SOAP_C_UTFSTRING: UTF-8 content in char*, SOAP_XML_INDENT: indent JSON */
struct soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
struct value *request = new_value(ctx);
struct value response;
... /* here we populate the request data to be send */
if (json_call(ctx, "endpoint URL", request, response))
... /* error */
... /* use the response data */
soap_end(ctx); /* delete all values */
... /* here we can make other calls etc. */
soap_free(ctx); /* delete the context */

The json_call function takes a context, an endpoint URL (with query string parameters as needed), and optional in and out values to send and receive, respectively. The function returns SOAP_OK (zero) for success or EOF, SOAP_SYNTAX_ERROR, or an HTTP error code.

To use the JSON REST POST method, pass both in and out values to json_call. For the GET method, pass a NULL to in. For the PUT method, pass a NULL to out. For the DELETE method, pass both NULL to in and out.

Besides json_call, there are other JSON API functions:

The are two other lower-level functions json_send and json_recv that are similar to json_write and json_read but do not initialize the sending and receiving operations and do not flush after the sending and receiving operations.

Compile and link together with soapC.c, xml-rpc.c, json.c, and stdsoap2.c.

Miscellaneous

Floating point format

The floating point format used to output values in XML-RPC and JSON is by default ".17lG' to ensure accuracy up to the last digit. The format can be set as follows:

struct soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
ctx->double_format = "%lG";

JSON and base64

JSON has no binary type to transmit binary data. Sending binary data in JSON strings as text is not recommended, due to NULs and problems with Unicode/UTF-8 sequences.

Base64 is a common encoding format for binary data. A JSON string with base64 content is our recommended option.

To populate JSON data with base64-encoded binary content, you can simply set the values as described earlier (e.g. by casting a _base64 structure to a value in C++). Receiving base64-encoded content with JSON is not possible, because the necessary type information is lost in transit. The base64 content will arrive at the receiver simply as a string with base64 content.

You can explicitly decode the base64 string back to binary as shown here for C++:

if (v.is_string())
{
/* assuming base64 content in string value v, decoded it */
int len;
unsigned char *ptr = soap_base642s(ctx, (const char*)v, NULL, 0, &len);
/* ptr points to binary of length len or is NULL when decoding failed */

And for C:

if (is_string(v))
{
/* assuming base64 content in string value v, decoded it */
int len;
unsigned char *ptr = soap_base642s(ctx, *string_of(v), NULL, 0, &len);
/* ptr points to binary of length len or is NULL when decoding failed */

JSON and ISO 8601 dateTime

To populate JSON data with ISO 8601 date time content, you can simply set the values as described earlier. Receiving ISO 8601 date time content with JSON is not possible, because the necessary type information is lost in transit. The content will arrive at the receiver simply as a string with a date and time.

You can explicitly convert a string with an ISO 8601 date time to a time_t value as shown here for C++:

if (v.is_string())
{
time_t tm;
if (soap_s2dateTime(ctx, (const char*)v, &tm) == SOAP_OK)
... // success

And for C:

if (is_string(v))
{
time_t tm;
if (soap_s2dateTime(ctx, *string_of(v), &tm) == SOAP_OK)
... // success