Building WSDXML sections
Last year I described the general structure of WSDAPI's WSDXML sections and provided some hints for creating and consuming these structures. I've received a few requests since then to provide more concrete examples of how to build these structures in code.
Before we get started, I do recommend you re-read my original post on WSDXML. The relationship between WSDXML_ELEMENT, WSDXML_TYPE, and WSDXML_NODE is an important part of understanding how everything fits together.
Simple example: element with text
The first example will demonstrate how to build a single element with text, but without any attributes or children. If we imagine the "ex" prefix is bound to the https://www.example.org namespace, here's the element we will want to build:
<ex:TestElement>text</ex:TestElement>
Creating this structure is straightforward. It involves two steps:
- Extract an XML name ("ex:TestElement") from the XML context
- Build a WSDXML_ELEMENT structure from that XML name
Here's what it looks like in code.
// All error handling is omitted from this example
HRESULT hr = S_OK;
IWSDXMLContext *pContext = NULL;
WSDXML_NAME *pName = NULL;
WSDXML_ELEMENT *pElement = NULL;
hr = WSDXMLCreateContext( &pContext );
hr = pContext->AddNamespace(
L"https://www.example.org",
L"ex",
NULL );
hr = pContext->AddNameToNamespace(
L"https://www.example.org",
L"TestElement",
&pName );
hr = WSDXMLBuildAnyForSingleElement(
pName, // pName is copied here, and must be freed separately
L"text",
&pElement );
// pElement is now complete, and contains: <ex:TestElement>text</ex:TestElement>
// cleanup:
// WSDFreeLinkedMemory( pElement );
// WSDFreeLinkedMemory( pName );
// pContext->Release();
Note that you can omit the "text" parameter to WSDXMLBuildAnyForSingleElement to generate empty elements. Supplying NULL here would result in <ex:TestElement />
Advanced example: chaining elements
We'll build on the last example to show how you can create chains of structures. Here's the element we want to build this time:
<ex:ParentElement>
<ex:ChildElement>text</ex:ChildElement>
<ex:AnotherChildElement />
</ex:ParentElement>
In this case, the steps go like this:
- Build ParentElement, ChildElement, and AnotherChildElement separately using the instructions above.
- Use WSDXMLAddChild to link ParentElement to ChildElement
- Use WSDXMLAddChild or WSDXMLAddSibling to add in AnotherChildElement. Either is acceptable.
Important: see section below on cleaning up WSDXML structures.Also note that you only need to call IWSDXMLContext::AddNamespace once for a given namespace--it is not necessary to call it for every name you add.
Here's some example code:
// Use steps above to create pParentElement, pChildElement,
// and pAnotherChildElement
hr = WSDXMLAddChild( pParentElement, pChildElement );
hr = WSDXMLAddSibling( pChildElement, pAnotherChildElement );
// It is also acceptable to call
// WSDXMLAddChild( pParentElement, pAnotherChildElement);
// cleanup:
// WSDFreeLinkedMemory( pParentElement );
// Use WSDFreeLinkedMemory to destroy WSDXML_NAME objects
// extracted from IWSDXMLContext
// DO NOT FREE pChildElement or pAnotherChildElement;
// they are attached to pParentElement
Destroying WSDXML structures
At first glance, it may seem complex to destroy these structures, but they're designed to be simple. You only need to follow a few simple rules to write robust and leak-free WSDXML code:
- All WSDXML functions use linked memory. Always use WSDFreeLinkedMemory to destroy individual structures or whole trees.
- WSDXMLBuildAnyForSingleElement copies the "name" parameter and links a copy into the new tree.
- WSDXMLAddChild and WSDXMLAddSibling link together all structures in the tree.
- Only free the topmost WSDXML_ELEMENT structure in an entire tree.
- When passing WSDXML structures as outparams from services, be sure to link the entire WSDXML tree into the outparameter.