gSOAP 数据操作

转自:https://github.com/stoneyrh/gSOAP/tree/master/gsoap/samples/xml-rpc-json

XML-RPC Support with Optional JSON Serialization
================================================

XML-RPC is a simple and effective messaging protocol. XML-RPC uses a generic
XML format to compose messages. XML serialization proceeds by marshaling
parameters in predefined XML elements. A simple type system is provided to
cover primitive types, structs, and arrays. That is, XML-RPC defines a couple
of frequently used XML types with common programming language equivalents.
XML-RPC does NOT provide a data binding to XML and does NOT implement a
validation mechanism to ensure that XML content is valid.

See http://www.xmlrpc.com for more details.

JSON (JavaScript Object Notation) is an even simpler data format that is highly
compatible across programming languages.

See http://www.json.org for more details.

C++ API for XML-RPC with JSON Support
-------------------------------------

The following source files are provided for XML-RPC support in C++:

  xml-rpc.h            XML-RPC bindings (gSOAP specification file for soapcpp2)
  xml-rpc.cpp          C++ XML-RPC API
  xml-rpc-io.h         C++ XML-RPC data serialization over streams
  xml-rpc-io.cpp       C++ XML-RPC data serialization over streams
  xml-rpc-iters.cpp    C++ iterators for structs, arrays, and parameters

For JSON serialization, use the following files instead of xml-rpc-io.h and
xml-rpc-io.cpp:

  json.h               C++ JSON serializer over streams
  json.cpp             C++ JSON serializer over streams

For JSON over HTTP (JSON REST method), please use the plugin/httppost.c plugin.
See JSON over HTTP explanation below.

Note that soapH.h, soapStub.h and soapC.cpp are required for XML-RPC and JSON.
To create these execute:

  > soapcpp2 -CSL xml-rpc.h

Also compile and link with stdsoap2.cpp (or libgsoap++.a installed by the
package).

C++ Examples
------------

Examples are provided in the software package:

  xml-rpc-currentTime.cpp	client in C++, also uses JSON to display time
  xml-rpc-currentTimeServer.cpp	server in C++
  xml-rpc-weblogs.cpp		client in C++
  xml-rpc-json.cpp		XML-RPC <=> JSON serialization example

See xml-rpc.h for the C++ member functions to create XML-RPC messages and
decode responses. These functions are intuitive casts, assignments, and
indexing functions.

An XML-RPC data value is created in C++ as follows, which requires a context
(ctx) for the engine state (stored in the soap struct) for communication,
memory management, and data allocation/deallocation:

  struct soap *ctx = soap_new();
  value v(ctx);

This creates an empty value. It can be assigned using any one of the following:

  v = 12345;          // an int
  v = 12.34;          // a double
  v = "abc";          // a string
  v = false;          // a Boolean
  v = time(0);        // a time_t value, serialized as XML-RPC dateTime element
  v[0] = 24;          // an array [24,99]
  v[1] = 99;
  v["name"] = "john"; // a struct {"name": "john", "age": 24}
  v["age"] = 24;

  _base64 img(ctx, 100, rawimage100bytes);
  v = img;            // a data block of 100 bytes (bas64)

You can combine this syntax to create arrays of structs etc:

  v[0]["name"] = "john"; // first array element is a struct with member "name"

To check the type of a value in C++, use:

  v.is_int()
  v.is_double()
  v.is_string()
  v.is_bool()
  v.is_true()
  v.is_false()
  v.is_dateTime()
  v.is_array()
  v.is_struct()
  v.is_base64()

And two array value methods:

  v.size(int)		// (re)set array size (pre-allocates)
  v.size()		// size of the array, if v is an array (0 otherwise)

To extract a value use casts or array/struct indexing:

  (int)v;
  (double)v;
  (char*)v
  (bool)v
  (time_t)v
  (_base64)v
  v[0]
  v["name"]

where _base64::ptr() points to the data of _base64::size() bytes.

Iterators can be used to traverse array, struct, and parameter content:

  param p;
  ... // populate p
  for (param::iterator i = p.begin(); i != p.end(); ++i)
    cout << (*i) << endl;

  value v;
  ... // populate v
  if (v.is_array())
  { _array &a = v;
    for (_array::iterator i = a.begin(); i != a.end(); ++i)
      cout << (*i) << endl;
  }
  else if (v.is_struct())
  { _struct &s = v;
    for (_array::iterator i = s.begin(); i != s.end(); ++i)
      cout << (*i) << endl;
  }

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

  soap_destroy(ctx); // delete objects
  soap_end(ctx);     // free temporary data

C++ XML-RCP Client Example
--------------------------

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

  #include "soapH.h" // generated by the command: soapcpp2 xml-rpc.h
  ...
  // create an allocation context
  soap *ctx = soap_new1(SOAP_C_UTFSTRING); // Store Unicode in UTF8 format
  // create a call object
  methodCall mycallobj(ctx, "endpoint URL", "methodXMLTagName");
  // create parameter list
  params input(ctx);
  // populate parameters
  input[0] = 123;           // first parameter is an integer
  input[1] = "abc";         // second is a string
  input[2]["name"] = "joe"; // a record, first member "name"
  input[2]["age"] = 23;     // a record, second member "age"
  input[3][0] = 456.789;    // an array, first element (a float)
  input[3][1] = "widget";   // an array, second element (a string)
  input[3][2] = true;       // an array, third element (a bool)
  value record(ctx);        // a value (record)
  record["foo"] = 1;
  record["bar"] = 2;
  input[4] = record;        // assign record to parameter
  ...
  // get array of parameters by making the XML-RPC call
  params output = mycallobj(input);
  // check result
  if (mycallobj.error())
    soap_print_fault(ctx, stderr);
  else if (output.empty())
    printf("No response data\n");
  else if (output.size() > 1)
    printf("More than one response data\n");
  else if (output[0].is_array() && !((_array)output[0]).empty())
    for (int i = 0; i < ((_array)output[0]).size())
      ... = output[0][i];
  else if (output[0].is_struct())
  { ... = output[0]["membername1"];
    ... = output[0]["membername2"];
  }
  else if (output[0].is_base64())
    _base64 raw = output[0];
  else if (output[0].is_bool())
    bool flag = output[0];
  else if (output[0].is_int())
    int num = output[0];
  else if (output[0].is_double())
    double num = output[0];
  else if (output[0].is_string())
    const char *str = output[0];
  else if (output[0].is_dateTime())
    time_t t = output[0];
  // deallocate all
  soap_destroy(ctx);
  soap_end(ctx);
  soap_free(ctx);

Alternatively, if desired the parameters of the methodCall object can be
directly populated as follows:

  methodCall mycallobj(ctx, "endpoint URL", "methodXMLTagName");
  // populate parameters
  mycallobj[0] = 123;           // first parameter is an integer
  mycallobj[1] = "abc";         // second is a string
  mycallobj[2]["name"] = "joe"; // a record, first member "name"
  mycallobj[2]["age"] = 23;     // a record, second member "age"
  mycallobj[3][0] = 456.789;    // an array, first element (a float)
  mycallobj[3][1] = "widget";   // an array, second element (a string)
  mycallobj[3][2] = true;       // an array, third element (a bool)
  mycallobj[4]["foo"] = 1;
  mycallobj[4]["bar"] = 2;

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

  if (output[0].is_int()) // can we cast to int?
    std::cout << "int: " << (int)output[0] << std::endl;

The above prints the integer value. This works as long as the type is checked
first. Casting to string (char*) converts atomic values and base64 data to
strings, but not compound types such as arrays and structs.

  std::cout << "value: " << (char*)output[0] << std::endl;

which prints a string representation of the int, double, boolean, time, or
base64 value. Nothing is printed for arrays and structs. Use iterators
(xml-rpc-iters.h) to walk over arrays and structs to print values. Or use the
JSON module to print values in JSON format, see further below.

C++ XML-RPC Data 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" // also compile and link xml-rpc-io.cpp
  ...
  std::cout << output[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" // also compile and link xml-rpc-io.cpp
  ...
  value input(ctx);
  std::cin >> input;

C++ JSON Serialization from/to Streams
--------------------------------------

To display values in JSON format or parse JSON data, use the json.h and
json.cpp JSON serializers. It is also possible to send and receive JSON data
over HTTP, but this requires some more coding (see JSON over HTTP below).

You can dump the XML-RPC data in JSON or populate XML-RPC from JSON data,
because the data stored in C++ is independent of XML-RPC and JSON formats.

For example:

  #include "json.h" // also compile and link json.cpp for JSON serialization
  ...
  soap *ctx = soap_new();
  value input(ctx);
  std::in >> input;                 // parse JSON, store as XML-PRC data
  params output = mycallobj(input); // make the XML-RPC call
  std::cout << output;              // display result in JSON

The JSON protocol has fewer data types than XML-RPC, so type information is
lost when serializing to JSON. XML-RPC distinguishes ints from floats while
JSON does not. XML-RPC also has a dateTime type and a raw binary data type
(base64) that JSON lacks. DateTime information is serialized as a string in
JSON and binary data is serialized as a base64-formatted string. Unicode is
stored in UTF8 format in strings. For compatibility with XML-RPC serialization
of UTF8-encoded strings, use the SOAP_C_UTFSTRING flag.

C++ JSON over HTTP (REST method)
--------------------------------

To serialize JSON over HTTP as a client application, use the plugin/httppost.c:

  #include "plugin/httppost.h"  // also compile and link plugin/httppost.c
  #include "json.h"             // also compile and link json.cpp
  ...
  soap *ctx = soap_new();
  value request(ctx);
  // now populate the 'request' data to send
  ...                           
  if (soap_post_connect(ctx, "URL", NULL, "application/json")
   || json_send(ctx, request)
   || soap_end_send(ctx))
    ... // error
  value response(ctx);
  if (soap_begin_recv(ctx)
   || json_recv(ctx, response)
   || soap_end_recv(ctx))
    ... // error
  // use the 'response' data response
  ...
  // dealloc objects and temp data
  soap_destroy(ctx);
  soap_end(ctx);
  // make other calls etc.
  ...
  // dealloc context
  soap_free(ctx);

C++ XML-RPC Server Example
--------------------------

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

  // create an allocation context
  soap *ctx = soap_new();
  // create a method object
  methodCall myobj(ctx);
  // parse it from stdin, fd, or current socket
  if (myobj.recv() != SOAP_OK)
    soap_print_fault(ctx, stderr);
  else
  {
    // create response
    methodResponse myresponse(ctx);
    // check method name
    if (!strcmp(myobj.name(), "methodXMLTagName"))
    { // method name matches: populate response parameters with values:
      myresponse[0] = ...;
      myresponse[1] = ...;
      ...
    }
    else
    { // otherwise, set fault
      myresponse.set_fault("Wrong method");
    }
    // send response
    if (myresponse.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);
  // free context (but we can reuse it to serve next call)
  soap_free(ctx);

The server code above uses standard in/out and thus runs over CGI. 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 (e.g.
samples/webserver.c):

  // create an allocation context
  soap *ctx = soap_new1(SOAP_IO_KEEPALIVE);
  // bind to port 8080
  if (!soap_valid_socket(soap_bind(ctx, NULL, 8080)))
    ... // error, stop
  // accept messages in server loop
  for (;;)
  { if (!soap_valid_socket(soap_accept(ctx)))
      ... // error, stop
    // create a method object
    methodCall myobj(ctx);
    // parse it from socket
    if (myobj.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 API for XML-RPC with JSON Support
-----------------------------------

The following source files are provided for XML-RPC support in C:

  xml-rpc.h            XML-RPC bindings (gSOAP specification file for soapcpp2)

For JSON serialization, use the following files instead of xml-rpc-io.h and
xml-rpc-io.cpp:

  json_c.h             C JSON serializer
  json_c.c             C JSON serializer

For JSON over HTTP (JSON REST method), please use the plugin/httppost.c plugin.
See JSON over HTTP explanation below.

Note that soapH.h and soapC.c are required for XML-RPC and JSON. To create
these execute:

  > soapcpp2 -c -CSL xml-rpc.h

Also compile and link with stdsoap2.c (or libgsoap.a installed by the package).

C Examples
----------

Examples are provided in the software package:

  xml-rpc-currentTime.c		client in C
  xml-rpc-weblogs.c		client in C

For C code, only the xml-rpc.h file is needed. To generate the XML-RPC bindings
in C, use:

soapcpp2 -c xml-rpc.h

As a consequence, all message manipulation is done at a very low-level.

An XML RPC call is made using the following function you can defined for
convenience::

  int methodCall(struct soap *soap, const char *URL, struct methodCall *m, struct methodResponse *r)
  { /* no namespaces */
    soap->namespaces = NULL;
    /* no SOAP encodingStyle */
    soap->encodingStyle = NULL;
    /* connect, send request, and receive response */
    if (soap_connect(soap, URL, NULL)
     || soap_begin_send(soap)
     || soap_put_methodCall(soap, m, "methodCall", NULL)
     || soap_end_send(soap)
     || soap_begin_recv(soap)
     || !soap_get_methodResponse(soap, r, "methodResponse", NULL)
     || soap_end_recv(soap))
      return soap_closesock(soap); /* closes socket and returns soap->error */
    soap_closesock(soap);
    return SOAP_OK;
  }

Use this XML-RPC method caller in C as follows:

  struct soap *soap = soap_new1(SOAP_C_UTFSTRING); /* support Unicode */
  struct methodCall m;
  struct methodResponse r;
  struct param p[4];  /* method has four parameters to send */
  int n;              /* an int */
  double x;           /* a float */
  struct _struct s;   /* a struct ... */
  struct member f[2]; /* ... with 2 members */
  struct _array a;    /* an array ... */
  struct value v[2];  /* ... with 2 values */
  _boolean False = 0, True = 1;
  /* Set up method call */
  m.methodName = "methodXMLTagName";
  /* set the four parameters */
  m.params.__size = 4;
  m.params.param = p;
  memset(p, 0, sizeof(p));
  p[0].value.__type = SOAP_TYPE__string;
  p[0].value.ref = "a string parameter";
  p[1].value.__type = SOAP_TYPE__int;
  p[1].value.ref = &n;
  n = 123;
  p[2].value.__type = SOAP_TYPE__double;
  p[2].value.ref = &x;
  x = 4.56;
  p[3].value.__type = SOAP_TYPE__struct;
  p[3].value.ref = &s;
  memset(&s, 0, sizeof(s));
  s.__size = 2;
  s.member = f;
  memset(f, 0, sizeof(f));
  f[0].name = "memberName1";
  f[0].value.__type = SOAP_TYPE__boolean;
  f[0].value.ref = &True;
  f[1].name = "memberName2";
  f[0].value.__type = SOAP_TYPE__array;
  f[0].value.ref = &a;
  memset(&a, 0, sizeof(a));
  a.data.__size = 2;
  a.data.value = v;
  memset(v, 0, sizeof(v));
  v[0].__type = SOAP_TYPE__string;
  v[0].ref = "hello";
  v[1].__type = SOAP_TYPE__string;
  v[1].ref = "world";
  /* connect, send request, and receive response */
  if (methodCall(soap, "http://domain/path/service", &m, &r))
  { soap_print_fault(soap, stderr);
    exit(soap->error);
  }
  if (r.fault)
  { /* print fault on stdout */
    soap_begin_send(soap);
    soap_put_fault(soap, r.fault, "fault", NULL);
    soap_end_send(soap);
  }
  else
  { /* print response parameters */
    int i;
    for (i = 0; i < r.params->__size; i++)
    { printf("Return parameter %d = ", i+1);
      display(&r.params->param[i].value); /* SEE BELOW */
      printf("\n");
    }
  }

To dynamically allocate data for automatic deallocation by the gSOAP engine,
use (instead of for example struct value v[2]):

  struct value *v = soap_malloc(soap, 2 * sizeof(struct value));
  memset(v, 0, 2 * sizeof(struct value));
  ...
  soap_end(soap); /* deallocate all */

See xml-rpc-currentTime.c and xml-rpc-weblogs.c for example C code.

A convenient way to display XML RPC data can be implemented as follows:

  void display(struct value *v)
  { int i;
    switch (v->__type)
    { case SOAP_TYPE__boolean:
        printf(*((char*)v->ref) ? "TRUE" : "FALSE");
        break;
      case SOAP_TYPE__double:
        printf("%g", *((double*)v->ref));
        break;
      case SOAP_TYPE__i4:
      case SOAP_TYPE__int:
        printf("%d", *((int*)v->ref));
        break;
      case SOAP_TYPE__dateTime_DOTiso8601:
        printf("%s", (char*)v->ref);
        break;
      case SOAP_TYPE__string:
        printf("\"%s\"", (char*)v->ref);
        break;
      case SOAP_TYPE__base64:
        printf("[%d bytes of raw data at %p]", ((struct _base64*)v->ref)->__size, ((struct _base64*)v->ref)->__ptr);
        break;
      case SOAP_TYPE__struct:
        printf("{\n");
        for (i = 0; i < ((struct _struct*)v->ref)->__size; i++)
        { printf("\"%s\":", ((struct _struct*)v->ref)->member[i].name);
          display(&((struct _struct*)v->ref)->member[i].value);
          printf(",\n");
        }
        printf("}\n");
        break;
      case SOAP_TYPE__array:
        printf("[\n");
        for (i = 0; i < ((struct _array*)v->ref)->data.__size; i++)
        { display(&((struct _array*)v->ref)->data.value[i]);
          printf(",\n");
        }
        printf("]\n");
        break;
      default:
        if (!v->__type)
          printf("\"%s\"", v->__any);
        else
          printf("{?}");
    }
  }

C JSON Serialization
--------------------

To display values in JSON format or parse JSON data, use the json_c.h and
json_c.c JSON serializers. It is also possible to send and receive JSON data
over HTTP.

You can dump the XML-RPC data in JSON or populate XML-RPC from JSON data,
because the data stored in C is independent of XML-RPC and JSON formats.

For example:

  #include "json_c.h" // also compile and link json.cpp for JSON serialization
  ...
  struct soap *ctx = soap_new();
  struct value v;
  soap->recvfd = ...;           // set file descriptor
  json_recv(soap, &v);          // parse JSON, store as XML-PRC data
  ...
  soap->sendfd = ...;           // set file descriptor
  json_send(soap, &v);          // output JSON value

The JSON protocol has fewer data types than XML-RPC, so type information is
lost when serializing to JSON. XML-RPC distinguishes ints from floats while
JSON does not. XML-RPC also has a dateTime type and a raw binary data type
(base64) that JSON lacks. DateTime information is serialized as a string in
JSON and binary data is serialized as a base64-formatted string. Unicode is
stored in UTF8 format in strings. For compatibility with XML-RPC serialization
of UTF8-encoded strings, use the SOAP_C_UTFSTRING flag.

C JSON over HTTP (REST method)
------------------------------

To serialize JSON over HTTP as a client application, use the plugin/httppost.c:

  #include "plugin/httppost.h"  // also compile and link plugin/httppost.c
  #include "json_c.h"           // also compile and link json_c.c
  ...
  struct soap *ctx = soap_new();
  struct value request;
  // now populate the 'request' data to send
  ...                           
  if (soap_post_connect(ctx, "URL", NULL, "application/json")
   || json_send(ctx, request)
   || soap_end_send(ctx))
    ... // error
  struct value response;
  if (soap_begin_recv(ctx)
   || json_recv(ctx, response)
   || soap_end_recv(ctx))
    ... // error
  // use the 'response' data response
  ...
  // dealloc objects and temp data
  soap_end(ctx);
  // make other calls etc.
  ...
  // dealloc context
  soap_free(ctx);
posted @ 2018-08-22 15:04  Reboost  阅读(199)  评论(0)    收藏  举报