This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Integration 6.4.1!

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.