WSDXML and WSDAPI's DOM sections
As I promised in my last post, here's a guide to the WSDXML_* structures used in DOM processing in WSDAPI. Before I get started, a quick statement on DOM: Document Object Model is a way of representing XML nodes as static structures in memory. The structure looks like a tree, and contains elements, attributes, and text. WSDAPI uses these DOM sections to represent any data it doesn't understand in a message; these sections are captured in the xs:any declarations in XML schema.
OK, on to the structures:
- WSDXML_TEXT represents a single blob of text inside an XML element.
- WSDXML_ELEMENT represents a single XML element, and is associated with an XML name. The element may contain attributes, element children, or text children.
- WSDXML_NODE is a base for both WSDXML_TEXT and WSDXML_ELEMENT. This type should never be used on its own.
- WSDXML_ATTRIBUTE represents a single attribute inside an XML element.
- WSDXML_NAME is a name stored inside a WSDXML_NAMESPACE. You can manage either structure without having to manage the other.
- And there are two remaining structures you can largely ignore: WSDXML_TYPE and WSDXML_PREFIX_MAPPING.
The WSDAPI XML parser will build trees of these structures to represent XML received in a message. If you want to build your own trees of arbitrary data to pass down into WSDAPI, use the following functions:
- WSDXMLBuildAnyForSingleElement builds a single element.
- WSDXMLAddChild binds a parent element to a child element.
- WSDXMLAddSibling adds a new sibling to an existing child element.
Although it's possible declare WSDXML_ELEMENT structures statically, I find that it's usually much easier to build them at runtime with WSDXMLBuildAnyForSingleElement.
These structures and functions aren't particularly complicated, but here are some hints to successfully create and consume WSDAPI DOM sections:
- Don't ever create a WSDXML_NODE structure on its own. Only create WSDXML_ELEMENT and WSDXML_TEXT.
- Do use WSDXML_NODE pointers to navigate the tree. Cast a WSDXML_NODE pointer to WSDXML_ELEMENT or WSDXML_TEXT, depending on the value of WSDXML_NODE.Type.
- Do use the helper functions outlined above.
- Do use WSDAPI's linked memory model when managing these structures. WSDAllocateLinkedMemory, WSDFreeLinkedMemory, etc. The helper functions require that you use linked memory.
- Don't build WSDXML_NAME structures on your own. Either use the WSDXMLBuildAnyForSingle function to create a whole WSDXML_ELEMENT, or use the IWSDXMLContext::AddNameToNamespace method to retrieve a pointer to a name in a known namespace.
- Don't use DOM when your structures already contain field for what an element you want to generate. WSDXML should only be used for adding elements that aren't defined in the original schema.
Now that you know how to parse and generate these trees, you need to know how to get access to them in generated SOAP messages. Not all fields in a SOAP message are extensible; xs:any has to be explicitly defined in a type, and WsdCodeGen will turn that declaration into a WSDXML_ELEMENT field in a structure or parameter list. In cases where xs:any is defined in an application-specific type (e.g., a type in WSD-Scan) these parameters will appear in generated code; in cases where they appear in native WSDAPI messages (WS-Discovery or WS-Eventing messages), you need to use advanced versions of WSDAPI methods or configure your generated code to expose them.
- Service-side WS-Discovery extensibility points are exposed in the Ex variants of IWSDiscoveryPublisher (e.g., PublishEx vs. Publish).
- Client-side WS-Discovery extensibility points are exposed in IWSDiscoveredService::GetExtendedDiscoXML.
- Device-side metadata points are accessible through the metadata structures and metadata management methods on IWSDDeviceHost.
- Client-side metadata points are accessible in the metadata structures returned by the metadata methods on IWSDDeviceProxy.
- WS-Eventing subscription extensibility points are accessible through generated code as described in my last post.
- Extensibility points in the SOAP header for arbitrary generated messages are accessible by altering generated code with the eventArg attribute in your codegen.config file.
And that's about it. So, now you know how these structures work, how to generate and consume them, and where you can access them in WSDAPI.