Waiting for engine...
Skip to main content

Manipulating data

You often need to manipulate the XML data when passing it to or receiving it from a SOAP web service to implement OperationTypes other than EXECUTE.

These manipulations range from simply wrapping the XML documents for batching in wrapper tags, to completely rewriting the XML for some web services. The most common manipulations are batching and un-batching.

  • You batch objects for OperationTypes like CREATE/UPDATE/UPSERT.

  • You un-batch objects returned from a QUERY, or the results of a CREATE/UPDATE/UPSERT.

Two utility classes in the Connector SDK Util project, XMLSplitter and XMLJoiner, make these common manipulations easier. These utilities simplify the amount of code to write and are generally the most efficient way to achieve their respective functions. For more complete examples using these classes, see the linked DemoUpdateOperation topic.

Joining XML multiple documents into a single XML document

Many of the Connector SDK operation requests provide an Iterator of data (either objects or ids). If the web service provides a way to execute the operation using a batch of objects, you can use the XMLJoiner.

Schema for a batch UPDATE operation

Schema example
<xs:schema>
<xs:complexType name="UpdateContactRequest">
<xs:sequence>
<xs:element name="Contact" type="Contact" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:complexType>
<xs:complexType name="Contact">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
<xs:complexType>
<xs:schema>

Code samples 1: Create a custom subclass of XMLJoiner

Code sample
public class ListXMLJoiner extends XMLJoiner
{
private final String _listElName;
private ElementWrapperEventGenerator _generator;

public ListXMLJoiner(String listElName, Result result) throws XMLStreamException
{
// "namespace repairing" does not work with dom output
super(createOutputFactory(false).createXMLEventWriter(result));
_listElName = listElName;
}

@Override
protected void startDocument() throws XMLStreamException
{
super.startDocument();

if(_generator == null) {
// the generator will create the necessary start/end events for the wrapping list element
_generator = new ElementWrapperEventGenerator(getEventFactory()).addWrapperElement(_listElName);
}

_generator.addStartEvents(getWriter());
}

@Override
protected void endDocument() throws XMLStreamException
{
_generator.addEndEvents(getWriter());
}
}

Code samples 2: Utilize this ListXMLJoiner to implement custom UPDATE operation

Code sample
@Override
protected void executeUpdate(UpdateRequest request, OperationResponse response)
{
for(List<ObjectData> updateBatch : RequestUtil.pageIterable(request, MAX_INPUT_RECORDS,
getContext().getConfig())) {

// create a DOM document containing a batch of request objects
Document requestDoc = DOMUtil.newDocumentNS();
ListXMLJoiner joiner = null;
try {
joiner = new ListXMLJoiner("UpdateContactRequest", new DOMResult(doc));
joiner.writeAll(updateBatch);
joiner.close();
} finally {
IOUtil.closeQuietly(joiner);
}

// ... now execute the batch update using the requestDoc ...
}
}

Splitting a single XML document into multiple XML documents

A web service that provides a QUERY operation returns an XML Document that is a list of multiple objects. If the web service provides batch CREATE/UPDATE/UPSERT operations, these operations are likely to return result documents that are also lists of result objects. In these cases, XMLSplitter is useful. Let’s see how XMLSplitter works.

The following schema is for a QUERY operation result. The QUERY operation returns the query results in a batch where each subsequent batch of results needs the "nextPageToken" from the previous batch. This is a best practice to avoid returning too many documents at one time.

Schema for a QUERY operation

Schema example
<xs:schema>
<xs:complexType name="QueryContactResponse">
<xs:sequence>
<xs:element name="Contact" type="Contact" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="nextPageToken" type="xs:string" use="optional"/>
<xs:complexType>
<xs:complexType name="Contact">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
<xs:complexType>
<xs:schema>

Code sample 1: Create a custom subclass of XMLSplitter:

Code sample
public class ListXMLSplitter extends XMLSplitter
{
private final String _listElName;
private String _nextPageToken;

public ListXMLSplitter(String listElName, Source source)
throws XMLStreamException
{
super(createInputFactory().createXMLEventReader(source));
_listElName = listElName;
}

public String getNextPageToken()
{
return _nextPageToken;
}

@Override
protected XMLEvent findNextObjectStart(boolean isFirst) throws XMLStreamException
{
if(isFirst) {

// there should be a list element wrapper
StartElement event = findNextElementStart(getReader());
if(!event.getName().getLocalPart().equals(_listElName)) {
thrownew IllegalStateException("Unexpected wrapper element " + event.getName());
}

// while we are here, grab the nextPageToken, if it exists
Attribute attr = event.getAttributeByName("nextPageToken");
if(attr != null) {
_nextPageToken = attr.getValue();
}
}

// skip to the next start-element event
return findNextElementStart(getReader());
}
}

Code sample 2: Utilize this ListXMLSplitter to implement custom QUERY operation:

Code sample
@Override
protected void executeQuery(QueryRequest request, OperationResponse response)
{
String nextPageToken = null;
do {

// construct query document using request query filter configuration and the nextPageToken if not-null
Document queryDoc = ...;

// ... execute the query request ...
WebServiceResponse wsResp = ... ;
ListXMLSplitter splitter = null;
try {

// split list into result documents
splitter = new ListXMLSplitter("QueryContactResponse", new DOMSource(wsResp.getResponseBodyDocument()));

for(Payload p : splitter) {
response.addPartialResult(filter, OperationStatus.SUCCESS, successCode, successMessage, p);
}

// determine if there are more pages of results
nextPageToken = splitter.getNextPageToken();

} finally {
IOUtil.closeQuietly(wsResp, splitter);
}

} while(nextPageToken != null);
}
On this Page