This was a challenge I must admit. There wasn't alot of API's to find that handles SOAP-requests. I ended up with one that I tried and failed and tried again. gSOAP. About gSOAP you can find here: www.genivia.com. I downloaded the source from sourceforge which is an opensource version. (I don't recommend installng from your OS-distributor. I yhink there were some source-files that were missing that you need). See here: https://sourceforge.net/projects/gsoap2/. Depending on your system, there are some options whn configuring, making and installing gSOAP. My system is Linux. I had to make sure I had Automake, Bison, Flex openssl, and zlib(everything). The INSTALL.txt file contains all information about how-to install for your specific system.

The source-files that I mentioned that were missing are "stdsoap2.c/cpp" and "stdsoap2.h". After making and isntalling you can find the files in "./gsoap" directory. They are needed when compiling.

I've "talked soap" in Talking SOAP with PHP and Python. Both of the API's here get all the methods/services by the WSDL-adresse of the SOAP-server. So does gSOAP. The only difference is that in gSOAP you have to use a couple of utilities to initiate it all and finally we have to compile it. In my example I'm going ti use 'C'. I'm  not comfortable with "C++" at the moment(syntax-wise), but I should think that C++ is much more intuitive, structure-wise, than using C becuase of class-creation and the benefits that come with that. In C there are only "structs", but the header-files/source-files are generously documented so it's not a problem.


There are two utilities:

  1. wsdl2h - this application, first of all, gets the methods defined by the SOAP-server and saves them in a header file.
  2. soapcpp2 - this application creates and binds the data to the header file created by wsdl2h.

 To create pure C code I write this at the command-line:

wsdl2h -c ongoing.h https://path_to_wsdl?WSDL

This just produces the header-file "ongoing.h" which contains all methods found at "https://path_to_wsdl?WSDL". 

Bind the data the the source-files by using soapcpp2. The -CL option signifies/produces methods for a client-application:

soapcpp2 -CL ongoing.h

This produces some files that you have to include in your build. Source-files made:

Saving soapStub.h annotated copy of the source input
Saving soapH.h declarations to #include
Saving ServiceSoap.nsmap namespace mapping table
Saving soapClient.c client proxy class
Saving soapC.c serializers

In addition to these you have to include stdsoap2.c and stdsoap2.h from "build-directoy/gsoap/". Copy them to your project-directory.

 

Just so it is clear. I'm going to do the same thing as I did with the PHP and Python examples(Talking SOAP with PHP and Python):

 

  1. Get some order-information from a specific order.
  2. Create an order by getting data from a xml-file.

1. Getting information from an allready-existing order

From the documentation of the SOAP-server I know that there's a method/service called "GetOrderByOrderNumber". qSOAP creates structs (in C) that's in the form "_<namespace>__<methodname>. In the header-file created (of my choice of name), I find this struct: _ns1__GetOrderByOrderNumber { .. };  Above the definition, as a comment, all functions connected (member-functions in a sense) to that method/structure. The first thing we have to do is start initializing:

struct soap *soap = soap_new(); // allocate and initialize a context
struct _ns1__GetOrderByOrderNumber* login = soap_new__ns1__GetOrderByOrderNumber(soap, 1);
struct _ns1__GetOrderByOrderNumberResponse res;

Line 1: Initialize a soap-context
Line 2: Allocate memory for the _ns1__GetOrderByOrderNumber by using one of the functions created by gSOAP. As fat as I understand the second param is "number of elements".
Line 3: This structure holds the response from the server.

_ns1__GetOrderByOrderNumber has four member-variables, containing login-info. I set these this way:

login->GoodsOwnerCode = malloc(20);
strcpy(login->GoodsOwnerCode, "owner-code");
printf("GoodsOwnerCode: %s\n", login->GoodsOwnerCode);
login->UserName = malloc(20);
strcpy(login->UserName, "username");
login->Password = malloc(20);
strcpy(login->Password, "user_password");
login->OrderNumber = malloc(20);
strcpy(login->OrderNumber, "109xx9");

With gSOAP there is also another version(safer?) of the function "strcpy", and everything that has to do with passwords should be "taken care of". Now that we have login-information we're going to do the soap-call and test the response from the soap-server. See code below:

if (soap_call___ns1__GetOrderByOrderNumber(soap, NULL, NULL, login, &res) == SOAP_OK)
{
	printf("Waybill is %s\n", res.GetOrderByOrderNumberResult->OrderInfo->WayBill);
	printf("Way of delivery is %s\n", res.GetOrderByOrderNumberResult->OrderInfo->WayOfDelivery);
	printf("Order status text is %s\n", res.GetOrderByOrderNumberResult->OrderInfo->OrderStatusText);
	printf("Number of items  is %i\n", res.GetOrderByOrderNumberResult->GoodsInfo->NumberOfPackages);
}
else
	soap_print_fault(soap, stderr);

The next step is to compile the source. I have to do that with some ssl-libraries. This is my commandline:

oslp@vxdwbsrvr:~/test/c> gcc -DWITH_OPENSSL -lssl -lcrypto -lz -lgsoapssl -o ongoing ongoing.c soapC.c soapClient.c stdsoap2.c

 

Output when running the program:

oslp@vxdwbsrvr:~/test/c> ./ongoing
Allocation of  _ns1__GetOrderByOrderNumber* went well
GoodsOwnerCode: owner-code
Waybill is 73325389902538911
Way of delivery is
Order status text is Sendt
Number of items  is 1

On line ´6' you can see that it's empty. Which just means that it's not set.

In the next example I'm going to write "how to create an order" using the 'C' interface and gSOAP.

2. Creating an order with info from an xml-file

In this example I'm going to create an order based on data from an xml-file. Below is part of the file. Before each tag-name(method/service) there's a namespace. This namespace is the same as the one used throughout the C-sourcefiles. This can be changed to whatever you like. I have just used the default that is generated by the command-line given earlier in this article.

<?xml version="1.0" encoding="UTF-8"?>
<ns1:CustomerOrder xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://ongoingsystems.se/WSI">
  <ns1:OrderInfo>
    <ns1:OrderIdentification>GoodsOwnerOrderNumber</ns1:OrderIdentification>
    <ns1:OrderOperation>CreateOrUpdate</ns1:OrderOperation>
    <ns1:GoodsOwnerOrderNumber>201904112046-test</ns1:GoodsOwnerOrderNumber>
    <ns1:ReferenceNumber>10077072</ns1:ReferenceNumber>
    <ns1:OrderRemark>mobil: 97501600-Test xml->obj->send request using gSOAP----FM</ns1:OrderRemark>
    <ns1:SalesCode>10077072</ns1:SalesCode>
    <ns1:DeliveryDate>2019-03-29</ns1:DeliveryDate>
    <ns1:OrderStatusCreated>300</ns1:OrderStatusCreated>
    <ns1:OrderStatusUpdated>300</ns1:OrderStatusUpdated>
  </ns1:OrderInfo>
  <ns1:GoodsOwner>
    <ns1:GoodsOwnerIdentification>SystemId</ns1:GoodsOwnerIdentification>
    <ns1:GoodsOwnerId>67</ns1:GoodsOwnerId>
  </ns1:GoodsOwner>
  <ns1:Customer>
    <ns1:CustomerIdentification>CustomerNumber</ns1:CustomerIdentification>
    <ns1:CustomerOperation>CreateOrUpdate</ns1:CustomerOperation>
*********************  Example  *******************************

First thing I do is create my source-file: corder.c. First thing we have to do is to initialize the correct structure

int main()
{
	struct soap *soap = soap_new(); // allocate and initialize a context
	struct _ns1__ProcessOrder* oinfo = soap_new__ns1__ProcessOrder(soap, 1);
	struct ns1__CustomerOrder* co = soap_new_ns1__CustomerOrder(soap, 1);
	struct _ns1__ProcessOrderResponse *response = soap_new__ns1__ProcessOrderResponse(soap, 1);
	
	if(!oinfo)
	{
		printf("Could not initialize. Exiting with error-number 1\n");
		exit(1);
	}
	else
	{
		printf("Initializing the _ns1__ProcessOrder-structure was a success.\n");
		printf("Setting login-info\n");
		initOinfo(oinfo);
	}

We use the same methods as in example 1.

  1. Initialize the soap-context
  2. Init the ProcessOrder structure. This structure contains variables with login information and a pointer to the ns1__CustomerOrder structure
  3. Allocate memory for ns1__CustomerOrder 
  4. Allocate memory for the _ns1__ProcessOrderResponse-structure which will hold the response from the SOAP-server

On line 17 I call a function initOinfo(...) that sets the login-information. It jsut sets username, password, ...etc. Same info as in the foregoing example. The next thing to do is to read the xml-file and insert the data into the ns1_CustomerOrder-structure/variable 'co'. I made a function called initOrderFromFile(...) that sets the structure. Here's the function:

int initOrderFromFile(struct soap* soap, struct ns1__CustomerOrder* co)
{
	FILE* fp;
	//open the file containing orderdata in xml-format(for reading)
	fp = fopen("create_order.xml", "r");
	//give the filepointer to the new soap-instance
	soap->recvfd = fileno(fp);
	//create and fill in the struct _ns1__CustomerOrder(see
	int error_code = soap_read_ns1__CustomerOrder(soap, co);
	if (soap_xml_error_check(error_code))
	{
		printf("There is an xml error.\n");
		printf("Output of soap_read: %i\n", error_code);
		fclose(fp);
		printf("Returning error-status 1.\n");
		return 1;
	}
	fclose(fp);
	soap->recvfd = 1; //write to stdout
	return 0;
}

Line 9 calls for a read-function. This too you can find when looking for the CustomerOrder-structure in the header-file that you created with gSOAP(wsdl2h and soapcpp). From 'main' I call this function and do a couple of tests to see if things are working.

if (initOrderFromFile(soap, co) == 0)//no errors
{
	if (co)
	{
		printf("Testing to see if the structure is a-ok.\n");
		printf("OrderInfo exists.. %s\n", co->OrderInfo->GoodsOwnerOrderNumber);
		printf("Customer??:\n");
		if (co->Customer->InvoiceAddress)
			printf("Invoice-adr->Mobile: %s\n", co->Customer->InvoiceAddress->MobilePhone);
		else
		{
			printf("Customer->InvoiceAddress is not a-ok\n");
		}
		//make the soap-call
	}

The next thing to do the soap-call. See code below:

//assign variable 'co' to the member-variable of _ns1__ProcessOrder - structure
oinfo->co = co;
if (soap_call___ns1__ProcessOrder(soap, NULL, NULL, oinfo, response) == SOAP_OK)
{
	if (response->ProcessOrderResult->Success)
	{
		printf("GoodsOwnerOrderNumber: %s\n", response->ProcessOrderResult->GoodsOwnerOrderNumber);
		printf("OrderId: %i\n", response->ProcessOrderResult->OrderId);
		printf("Message from server: %s\n", response->ProcessOrderResult->Message);
	}
	else
		printf("Errors: %s\n", response->ProcessOrderResult->ErrorMessage);
}

Print some info to see if things are "a-ok". And always remember to free you variables:

free(response);
free(co);
free(oinfo);
soap_free(soap);

And as with the las example I use the same parameters to compile:

oslp@vxdwbsrvr:~/test/c> gcc -DWITH_OPENSSL -lssl -lcrypto -lz -lgsoapssl -o corder corder.c soapC.c soapClient.c stdsoap2.c

 

The output of this is the following; when everything is ok and you execute the program:

oslp@vxdwbsrvr:~/test/c> ./corder
Initializing the _ns1__ProcessOrder-structure was a success.
Setting login-info
GoodsOwnerCode is: profag test
UserName is: owt
Testing to see if the structure is a-ok.
OrderInfo exists.. 201904112046-test
Customer??:
Invoice-adr->Mobile:  / 21601818
GoodsOwnerOrderNumber: 201904112046-test
OrderId: 21696
Message from server: Customer with customer number '31750' and ID '27475' created. <br/> <br/>Order number 201904112046-test created/updated