diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathTransformerParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathTransformerParser.java
index 629c265c60..d00aa3fc95 100644
--- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathTransformerParser.java
+++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathTransformerParser.java
@@ -41,9 +41,19 @@ public class XPathTransformerParser extends AbstractTransformerParser {
@Override
protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String expression = element.getAttribute("expression");
- Assert.isTrue(StringUtils.hasText(expression), "The 'expression' attribute is required.");
- builder.addConstructorArgValue(expression);
+ String expressionRef = element.getAttribute("expression-ref");
+ boolean hasRef = StringUtils.hasText(expressionRef);
+ Assert.isTrue(hasRef ^ StringUtils.hasText(expression),
+ "Exactly one of the 'expression' or 'expression-ref' attributes is required.");
+ if (hasRef) {
+ builder.addConstructorArgReference(expressionRef);
+ }
+ else {
+ builder.addConstructorArgValue(expression);
+ }
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "evaluation-type");
+ IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "node-mapper");
+ IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "converter");
}
}
diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XPathTransformer.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XPathTransformer.java
index e1824cfbf9..4199f67b9b 100644
--- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XPathTransformer.java
+++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XPathTransformer.java
@@ -16,9 +16,6 @@
package org.springframework.integration.xml.transformer;
-import java.util.HashMap;
-import java.util.Map;
-
import org.w3c.dom.Node;
import org.springframework.integration.core.Message;
@@ -34,6 +31,13 @@ import org.springframework.xml.xpath.XPathExpressionFactory;
/**
* Transformer implementation that evaluates an XPath expression against the inbound
* Message payload and returns a Message whose payload is the result of that evaluation.
+ * Prior to evaluation, the payload may be converted by the configured {@link XmlPayloadConverter}
+ * instance. The default converter type is {@link DefaultXmlPayloadConverter}.
+ *
+ * The evaluation result type will depend on either the enumeration value provided to
+ * {@link #setEvaluationType(XPathEvaluationType)} or the presence of a {@link NodeMapper},
+ * which takes precedence. If no {@link NodeMapper} or evaluation type is configured explicitly,
+ * the default evaluation type is {@link XPathEvaluationType#STRING_RESULT}.
*
* @author Mark Fisher
* @since 2.0
@@ -49,18 +53,21 @@ public class XPathTransformer extends AbstractTransformer {
private volatile NodeMapper nodeMapper;
+ /**
+ * Create an {@link XPathTransformer} that will create an XPath expression from the given String
+ * to be evaluated against converted inbound Message payloads.
+ */
public XPathTransformer(String expression) {
this.xpathExpression = XPathExpressionFactory.createXPathExpression(expression);
}
- public XPathTransformer(String expression, Map namespaces) {
- this.xpathExpression = XPathExpressionFactory.createXPathExpression(expression, namespaces);
- }
-
- public XPathTransformer(String expression, String prefix, String namespace) {
- Map namespaces = new HashMap();
- namespaces.put(prefix, namespace);
- this.xpathExpression = XPathExpressionFactory.createXPathExpression(expression, namespaces);
+ /**
+ * Create an {@link XPathTransformer} that will evaluate the given {@link XPathExpression}
+ * against converted inbound Message payloads.
+ */
+ public XPathTransformer(XPathExpression expression) {
+ Assert.notNull(expression, "expression must not be null");
+ this.xpathExpression = expression;
}
@@ -68,16 +75,16 @@ public class XPathTransformer extends AbstractTransformer {
* Specify the expected {@link XPathEvaluationType}. The default is {@link XPathEvaluationType#STRING_RESULT}.
*/
public void setEvaluationType(XPathEvaluationType evaluationType) {
+ Assert.notNull(evaluationType, "evaluationType must not be null.");
this.evaluationType = evaluationType;
}
/**
- * Set a {@link NodeMapper} to use for generating the result object.
- * This will also set the evaluationType to null since
- * the actual type determination is a responsibility of the NodeMapper.
+ * Set a {@link NodeMapper} to use for generating the result object. By default the NodeMapper is null,
+ * but if explicitly set, type determination is the responsibility of the NodeMapper, taking precedence
+ * over any configured evaluationType.
*/
public void setNodeMapper(NodeMapper nodeMapper) {
- this.evaluationType = null;
this.nodeMapper = nodeMapper;
}
@@ -93,8 +100,7 @@ public class XPathTransformer extends AbstractTransformer {
protected Object doTransform(Message> message) throws Exception {
Node node = this.converter.convertToNode(message.getPayload());
Object result = null;
- if (evaluationType == null) {
- Assert.notNull(this.nodeMapper, "NodeMapper required if evaluationType is null.");
+ if (this.nodeMapper != null) {
result = this.xpathExpression.evaluateAsObject(node, this.nodeMapper);
}
else {
diff --git a/spring-integration-xml/src/main/resources/org/springframework/integration/xml/config/spring-integration-xml-2.0.xsd b/spring-integration-xml/src/main/resources/org/springframework/integration/xml/config/spring-integration-xml-2.0.xsd
index 10afde02a7..2a8dfd8d35 100644
--- a/spring-integration-xml/src/main/resources/org/springframework/integration/xml/config/spring-integration-xml-2.0.xsd
+++ b/spring-integration-xml/src/main/resources/org/springframework/integration/xml/config/spring-integration-xml-2.0.xsd
@@ -166,13 +166,27 @@
-
+
- The XPath expression to be evaluated against the input Message's payload.
+ The XPath expression string to be evaluated against the input Message's payload.
+ Either this or 'expression-ref' must be provided, but not both.
+
+
+
+ Reference to the XPathExpression instance to be evaluated against the input Message's payload.
+ Either this or 'expression' must be provided, but not both.
+
+
+
+
+
+
+
+
@@ -182,13 +196,39 @@
+
+
-
-
+
+
+
+ Reference to a NodeMapper. If this is provided, the 'evaluation-type' will be ignored. Instead, the
+ org.springframework.xml.xpath.XPathExpression's evaluateAsObject(Node node, NodeMapper nodeMapper)
+ method will be invoked.
+
+
+
+
+
+
+
+
+
+
+
+ Specify the XmlPayloadConverter to use when converting a Message payload prior to XPath evaluation.
+
+
+
+
+
+
+
+
diff --git a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathTransformerParserTests-context.xml b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathTransformerParserTests-context.xml
index 0b7b7caa91..48e78380e2 100644
--- a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathTransformerParserTests-context.xml
+++ b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathTransformerParserTests-context.xml
@@ -24,4 +24,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathTransformerParserTests.java b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathTransformerParserTests.java
index f2c95fc047..a5fabaff78 100644
--- a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathTransformerParserTests.java
+++ b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathTransformerParserTests.java
@@ -19,19 +19,28 @@ package org.springframework.integration.xml.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import java.io.StringReader;
import java.util.List;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Source;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.channel.PollableChannel;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.message.MessageBuilder;
+import org.springframework.integration.xml.XmlPayloadConverter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.xml.xpath.NodeMapper;
/**
* @author Mark Fisher
@@ -56,6 +65,15 @@ public class XPathTransformerParserTests {
@Autowired
private MessageChannel nodeListInput;
+ @Autowired
+ private MessageChannel nodeMapperInput;
+
+ @Autowired
+ private MessageChannel customConverterInput;
+
+ @Autowired
+ private MessageChannel expressionRefInput;
+
@Autowired
private PollableChannel output;
@@ -99,4 +117,55 @@ public class XPathTransformerParserTests {
assertEquals(3, nodeList.size());
}
+ @Test
+ public void nodeMapper() {
+ this.nodeMapperInput.send(message);
+ assertEquals("42-mapped", output.receive(0).getPayload());
+ }
+
+ @Test
+ public void customConverter() {
+ this.customConverterInput.send(message);
+ assertEquals("custom", output.receive(0).getPayload());
+ }
+
+ @Test
+ public void expressionRef() {
+ this.expressionRefInput.send(message);
+ assertEquals(new Double(84), output.receive(0).getPayload());
+ }
+
+
+ @SuppressWarnings("unused")
+ private static class TestNodeMapper implements NodeMapper {
+
+ @Override
+ public Object mapNode(Node node, int nodeNum) throws DOMException {
+ return node.getTextContent() + "-mapped";
+ }
+ }
+
+
+ @SuppressWarnings("unused")
+ private static 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("")));
+ }
+ catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public Document convertToDocument(Object object) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
}
diff --git a/spring-integration-xml/src/test/java/org/springframework/integration/xml/transformer/XPathTransformerTests.java b/spring-integration-xml/src/test/java/org/springframework/integration/xml/transformer/XPathTransformerTests.java
index 8f1f24b5d8..1ca2ddaf26 100644
--- a/spring-integration-xml/src/test/java/org/springframework/integration/xml/transformer/XPathTransformerTests.java
+++ b/spring-integration-xml/src/test/java/org/springframework/integration/xml/transformer/XPathTransformerTests.java
@@ -19,17 +19,26 @@ package org.springframework.integration.xml.transformer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import java.io.StringReader;
import java.util.List;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Source;
+
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
import org.springframework.integration.core.Message;
import org.springframework.integration.message.MessageBuilder;
+import org.springframework.integration.xml.XmlPayloadConverter;
import org.springframework.integration.xml.xpath.XPathEvaluationType;
import org.springframework.xml.xpath.NodeMapper;
+import org.springframework.xml.xpath.XPathExpression;
+import org.springframework.xml.xpath.XPathExpressionFactory;
/**
* @author Mark Fisher
@@ -55,6 +64,14 @@ public class XPathTransformerTests {
assertEquals("test", result);
}
+ @Test
+ public void xpathExpressionReferenceConstructorInsteadOfString() throws Exception {
+ XPathExpression expression = XPathExpressionFactory.createXPathExpression("/parent/child/@name");
+ XPathTransformer transformer = new XPathTransformer(expression);
+ Object result = transformer.doTransform(message);
+ assertEquals("test", result);
+ }
+
@Test
public void numberResult() throws Exception {
XPathTransformer transformer = new XPathTransformer("/parent/child/@age");
@@ -111,6 +128,14 @@ public class XPathTransformerTests {
assertEquals("test-mapped", result);
}
+ @Test
+ public void customConverter() throws Exception {
+ XPathTransformer transformer = new XPathTransformer("/test/@type");
+ transformer.setConverter(new TestXmlPayloadConverter());
+ Object result = transformer.doTransform(message);
+ assertEquals("custom", result);
+ }
+
private static class TestNodeMapper implements NodeMapper {
@@ -120,4 +145,26 @@ public class XPathTransformerTests {
}
}
+
+ private static 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("")));
+ }
+ catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public Document convertToDocument(Object object) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
}