Transforming XML Messages with XPath

When it comes to message transformation, XPath is a great way to transform messages that have XML payloads. You can do so by defining XPath transformers with the <xpath-transformer/> element.

Simple XPath Transformation

Consider following transformer configuration:

<int-xml:xpath-transformer input-channel="inputChannel" output-channel="outputChannel"
      xpath-expression="/person/@name" />

Also consider the following Message:

Message<?> message =
  MessageBuilder.withPayload("<person name='John Doe' age='42' married='true'/>").build();

After sending this message to the 'inputChannel', the XPath transformer configured earlier transforms this XML Message to a simple Message with a payload of 'John Doe', all based on the simple XPath Expression specified in the xpath-expression attribute.

XPath also lets you perform simple conversion of an extracted element to a desired type. Valid return types are defined in javax.xml.xpath.XPathConstants and follow the conversion rules specified by the javax.xml.xpath.XPath interface.

The following constants are defined by the XPathConstants class: BOOLEAN, DOM_OBJECT_MODEL, NODE, NODESET, NUMBER, and STRING.

You can configure the desired type by using the evaluation-type attribute of the <xpath-transformer/> element, as the following example shows (twice):

<int-xml:xpath-transformer input-channel="numberInput" xpath-expression="/person/@age"
                           evaluation-type="NUMBER_RESULT" output-channel="output"/>

<int-xml:xpath-transformer input-channel="booleanInput"
                           xpath-expression="/person/@married = 'true'"
                           evaluation-type="BOOLEAN_RESULT" output-channel="output"/>

Node Mappers

If you need to provide custom mapping for the node extracted by the XPath expression, you can provide a reference to the implementation of the org.springframework.xml.xpath.NodeMapper (an interface used by XPathOperations implementations for mapping Node objects on a per-node basis). To provide a reference to a NodeMapper, you can use the node-mapper attribute, as the following example shows:

<int-xml:xpath-transformer input-channel="nodeMapperInput" xpath-expression="/person/@age"
                           node-mapper="testNodeMapper" output-channel="output"/>

The following example shows a NodeMapper implementation that works with the preceding example:

class TestNodeMapper implements NodeMapper {
  public Object mapNode(Node node, int nodeNum) throws DOMException {
    return node.getTextContent() + "-mapped";
  }
}

XML Payload Converter

You can also use an implementation of the org.springframework.integration.xml.XmlPayloadConverter to provide more granular transformation. The following example shows how to define one:

<int-xml:xpath-transformer input-channel="customConverterInput"
                           output-channel="output" xpath-expression="/test/@type"
                           converter="testXmlPayloadConverter" />

The following example shows an XmlPayloadConverter implementation that works with the preceding example:

class TestXmlPayloadConverter implements XmlPayloadConverter {
  public Source convertToSource(Object object) {
    throw new UnsupportedOperationException();
  }
  //
  public Node convertToNode(Object object) {
    try {
      return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
          new InputSource(new StringReader("<test type='custom'/>")));
    }
    catch (Exception e) {
      throw new IllegalStateException(e);
    }
  }
  //
  public Document convertToDocument(Object object) {
    throw new UnsupportedOperationException();
  }
}

If you do not provide this reference, the DefaultXmlPayloadConverter is used. It should suffice in most cases, because it can convert from Node, Document, Source, File, String, InputStream, and byte[] payloads. If you need to extend beyond the capabilities of that default implementation, an upstream Transformer is probably a better option than providing a reference to a custom implementation of this strategy here.