INT-3140: Add #xpath() SpEL Function Support
JIRA: https://jira.springsource.org/browse/INT-3140 * Introduce `XPathUtils` * Add `#xpath()` tests * Add documentation INT-3140: Polishing according PR comments INT-3140 Polishing - Doc and javadoc polishing - Fix some compiler warnings (not all xpath)
This commit is contained in:
committed by
Gary Russell
parent
fa59b50bdc
commit
88de8117aa
@@ -174,6 +174,31 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa
|
||||
}
|
||||
}
|
||||
|
||||
String xpathBeanName = "xpath";
|
||||
alreadyRegistered = false;
|
||||
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
|
||||
alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBean(xpathBeanName);
|
||||
}
|
||||
else {
|
||||
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(xpathBeanName);
|
||||
}
|
||||
if (!alreadyRegistered) {
|
||||
Class<?> xpathClass = null;
|
||||
try {
|
||||
xpathClass = ClassUtils.forName(IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils",
|
||||
parserContext.getReaderContext().getBeanClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
logger.debug("SpEL function '#xpath' isn't registered: there is no spring-integration-xml.jar on the classpath.");
|
||||
}
|
||||
|
||||
if (xpathClass != null) {
|
||||
IntegrationNamespaceUtils.registerSpelFunctionBean(parserContext.getRegistry(), xpathBeanName,
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", "evaluate");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.doRegisterBuiltInBeans(parserContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,7 @@ public class ChainParserTests {
|
||||
final AtomicReference<String> log = new AtomicReference<String>();
|
||||
when(logger.isWarnEnabled()).thenReturn(true);
|
||||
doAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
log.set((String) invocation.getArguments()[0]);
|
||||
return null;
|
||||
@@ -394,7 +395,7 @@ public class ChainParserTests {
|
||||
assertTrue(this.beanFactory.containsBean("recipientListRouterChain$child.recipientListRouterWithinChain.handler"));
|
||||
|
||||
MessageHandlerChain chain = this.beanFactory.getBean("headerEnricherChain.handler", MessageHandlerChain.class);
|
||||
List handlers = TestUtils.getPropertyValue(chain, "handlers", List.class);
|
||||
List<?> handlers = TestUtils.getPropertyValue(chain, "handlers", List.class);
|
||||
|
||||
assertTrue(handlers.get(0) instanceof MessageTransformingHandler);
|
||||
assertEquals("headerEnricherChain$child.headerEnricherWithinChain", TestUtils.getPropertyValue(handlers.get(0), "componentName"));
|
||||
|
||||
@@ -28,14 +28,11 @@ import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
import com.jayway.jsonpath.Criteria;
|
||||
import com.jayway.jsonpath.Filter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -51,8 +48,12 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
import com.jayway.jsonpath.Criteria;
|
||||
import com.jayway.jsonpath.Filter;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*/
|
||||
@ContextConfiguration(classes = JsonPathTests.JsonPathTestsContextConfiguration.class, loader = AnnotationConfigContextLoader.class)
|
||||
@@ -69,7 +70,9 @@ public class JsonPathTests {
|
||||
public static void setUp() throws IOException {
|
||||
ClassPathResource jsonResource = new ClassPathResource("JsonPathTests.json", JsonPathTests.class);
|
||||
JSON_FILE = jsonResource.getFile();
|
||||
JSON = new Scanner(JSON_FILE).useDelimiter("\\Z").next();
|
||||
Scanner scanner = new Scanner(JSON_FILE);
|
||||
JSON = scanner.useDelimiter("\\Z").next();
|
||||
scanner.close();
|
||||
testMessage = new GenericMessage<String>(JSON);
|
||||
}
|
||||
|
||||
@@ -208,7 +211,7 @@ public class JsonPathTests {
|
||||
public static class JsonPathTestsContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public Filter jsonPathFilter() {
|
||||
public Filter<?> jsonPathFilter() {
|
||||
return Filter.filter(Criteria.where("isbn").exists(true).and("category").ne("fiction"));
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ public class SpelTransformerIntegrationTests {
|
||||
|
||||
@Override
|
||||
public Class<?>[] getSpecificTargetClasses() {
|
||||
return new Class[] {Foo.class};
|
||||
return new Class<?>[] {Foo.class};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.xml.xpath;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import org.springframework.integration.xml.DefaultXmlPayloadConverter;
|
||||
import org.springframework.integration.xml.XmlPayloadConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.xml.xpath.NodeMapper;
|
||||
import org.springframework.xml.xpath.XPathException;
|
||||
import org.springframework.xml.xpath.XPathExpression;
|
||||
import org.springframework.xml.xpath.XPathExpressionFactory;
|
||||
|
||||
/**
|
||||
* Utility class for 'xpath' support.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class XPathUtils {
|
||||
|
||||
public static final String STRING = "string";
|
||||
|
||||
public static final String BOOLEAN = "boolean";
|
||||
|
||||
public static final String NUMBER = "number";
|
||||
|
||||
public static final String NODE = "node";
|
||||
|
||||
public static final String NODE_LIST = "node_list";
|
||||
|
||||
public static final String DOCUMENT_LIST = "document_list";
|
||||
|
||||
private static List<String> RESULT_TYPES = Arrays.asList(STRING, BOOLEAN, NUMBER, NODE, NODE_LIST, DOCUMENT_LIST);
|
||||
|
||||
private static XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
|
||||
|
||||
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
|
||||
/**
|
||||
* Utility method to evaluate an xpath on the provided object.
|
||||
* Delegates evaluation to an {@link XPathExpression}.
|
||||
* Note this method provides the {@code #xpath()} SpEL function.
|
||||
*
|
||||
*
|
||||
* @param o the xml Object for evaluaton.
|
||||
* @param xpath an 'xpath' expression String.
|
||||
* @param resultArg an optional parameter to represent the result type of the xpath evaluation.
|
||||
* Only one argument is allowed, which can be an instance of {@link org.springframework.xml.xpath.NodeMapper} or
|
||||
* one of these String constants: "string", "boolean", "number", "node" or "node_list".
|
||||
* @return the result of the xpath expression evaluation.
|
||||
* @throws IllegalArgumentException - if the provided arguments aren't appropriate types or values;
|
||||
* @throws MessagingException - if the provided object can't be converted to a {@link Node};
|
||||
* @throws XPathException - if the xpath expression can't be evaluated.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static <T> T evaluate(Object o, String xpath, Object... resultArg) {
|
||||
Object resultType = null;
|
||||
if (resultArg != null && resultArg.length > 0) {
|
||||
Assert.isTrue(resultArg.length == 1, "'resultArg' can contains only one element.");
|
||||
Assert.noNullElements(resultArg, "'resultArg' can't contains 'null' elements.");
|
||||
resultType = resultArg[0];
|
||||
}
|
||||
|
||||
XPathExpression expression = XPathExpressionFactory.createXPathExpression(xpath);
|
||||
Node node = converter.convertToNode(o);
|
||||
|
||||
if (resultType == null) {
|
||||
return (T) expression.evaluateAsString(node);
|
||||
}
|
||||
else if (resultType instanceof NodeMapper<?>) {
|
||||
return (T) expression.evaluateAsObject(node, (NodeMapper<?>) resultType);
|
||||
}
|
||||
else if (resultType instanceof String && RESULT_TYPES.contains(resultType)) {
|
||||
String resType = (String) resultType;
|
||||
if (DOCUMENT_LIST.equals(resType)) {
|
||||
List<Node> nodeList = (List<Node>) XPathEvaluationType.NODE_LIST_RESULT.evaluateXPath(expression, node);
|
||||
try {
|
||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
List<Node> documents = new ArrayList<Node>(nodeList.size());
|
||||
for (Node n : nodeList) {
|
||||
Document document = documentBuilder.newDocument();
|
||||
document.appendChild(document.importNode(n, true));
|
||||
documents.add(document);
|
||||
}
|
||||
return (T) documents;
|
||||
}
|
||||
catch (ParserConfigurationException e) {
|
||||
throw new XPathException("Unable to create 'documentBuilder'.", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
XPathEvaluationType evaluationType = XPathEvaluationType.valueOf(resType.toUpperCase() + "_RESULT");
|
||||
return (T) evaluationType.evaluateXPath(expression, node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("'resultArg[0]' can be an instance of 'NodeMapper<?>' " +
|
||||
"or one of supported String constants: " + RESULT_TYPES);
|
||||
}
|
||||
}
|
||||
|
||||
private XPathUtils() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<int:channel id="channelA">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int:channel id="channelB">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int:channel id="channelZ">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int:transformer input-channel="xpathTransformerInput" output-channel="channelA"
|
||||
expression="#xpath(payload, headers.xpath, @testNodeMapper)"/>
|
||||
|
||||
<int:filter input-channel="xpathFilterInput" output-channel="channelA" discard-channel="channelZ"
|
||||
expression="#xpath(payload, '/name', 'boolean')"/>
|
||||
|
||||
<int:splitter input-channel="xpathSplitterInput" output-channel="channelA"
|
||||
expression="#xpath(payload, '//book', 'document_list')"/>
|
||||
|
||||
<int:router input-channel="xpathRouterInput"
|
||||
expression="#xpath(payload, '/name')"
|
||||
resolution-required="false"
|
||||
default-output-channel="channelZ">
|
||||
<int:mapping value="A" channel="channelA"/>
|
||||
<int:mapping value="B" channel="channelB"/>
|
||||
</int:router>
|
||||
|
||||
<bean id="testNodeMapper" class="org.springframework.integration.xml.xpath.XPathTests$TestNodeMapper"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.xml.xpath;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.integration.xml.xpath.XPathUtils.evaluate;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.xml.source.StringSourceFactory;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.xml.xpath.NodeMapper;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class XPathTests {
|
||||
|
||||
private static final String XML = "<parent><child name='test' age='42' married='true'/></parent>";
|
||||
|
||||
@Autowired
|
||||
private PollableChannel channelA;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel channelB;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel channelZ;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel xpathTransformerInput;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel xpathFilterInput;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel xpathSplitterInput;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel xpathRouterInput;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testXPathUtils() {
|
||||
Object result = evaluate(XML, "/parent/child/@name");
|
||||
assertEquals("test", result);
|
||||
|
||||
result = evaluate(XML, "/parent/child/@name", "string");
|
||||
assertEquals("test", result);
|
||||
|
||||
result = evaluate(XML, "/parent/child/@age", "number");
|
||||
assertEquals((double) 42, result);
|
||||
|
||||
result = evaluate(XML, "/parent/child/@married = 'true'", "boolean");
|
||||
assertEquals(Boolean.TRUE, result);
|
||||
|
||||
result = evaluate(XML, "/parent/child", "node");
|
||||
assertThat(result, Matchers.instanceOf(Node.class));
|
||||
Node node = (Node) result;
|
||||
assertEquals("child", node.getLocalName());
|
||||
assertEquals("test", node.getAttributes().getNamedItem("name").getTextContent());
|
||||
assertEquals("42", node.getAttributes().getNamedItem("age").getTextContent());
|
||||
assertEquals("true", node.getAttributes().getNamedItem("married").getTextContent());
|
||||
|
||||
result = evaluate("<parent><child name='foo'/><child name='bar'/></parent>", "/parent/child", "node_list");
|
||||
assertThat(result, Matchers.instanceOf(List.class));
|
||||
List<Node> nodeList = (List<Node>) result;
|
||||
assertEquals(2, nodeList.size());
|
||||
Node node1 = nodeList.get(0);
|
||||
Node node2 = nodeList.get(1);
|
||||
assertEquals("child", node1.getLocalName());
|
||||
assertEquals("foo", node1.getAttributes().getNamedItem("name").getTextContent());
|
||||
assertEquals("child", node2.getLocalName());
|
||||
assertEquals("bar", node2.getAttributes().getNamedItem("name").getTextContent());
|
||||
|
||||
result = evaluate("<parent><child name='foo'/><child name='bar'/></parent>", "/parent/child", "document_list");
|
||||
assertThat(result, Matchers.instanceOf(List.class));
|
||||
List<Document> documentList = (List<Document>) result;
|
||||
assertEquals(2, documentList.size());
|
||||
Node document1 = documentList.get(0);
|
||||
Node document2 = documentList.get(1);
|
||||
assertEquals("child", document1.getFirstChild().getLocalName());
|
||||
assertEquals("foo", document1.getFirstChild().getAttributes().getNamedItem("name").getTextContent());
|
||||
assertEquals("child", document2.getFirstChild().getLocalName());
|
||||
assertEquals("bar", document2.getFirstChild().getAttributes().getNamedItem("name").getTextContent());
|
||||
|
||||
result = evaluate(XML, "/parent/child/@name", new TestNodeMapper());
|
||||
assertEquals("test-mapped", result);
|
||||
|
||||
try {
|
||||
evaluate(new Date(), "/parent/child");
|
||||
fail("MessagingException expected.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, Matchers.instanceOf(MessagingException.class));
|
||||
assertThat(e.getMessage(), Matchers.containsString("unsupported payload type"));
|
||||
}
|
||||
|
||||
try {
|
||||
evaluate(XML, "/parent/child", "string", "number");
|
||||
fail("MessagingException expected.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, Matchers.instanceOf(IllegalArgumentException.class));
|
||||
assertEquals("'resultArg' can contains only one element.", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
evaluate(XML, "/parent/child", "foo");
|
||||
fail("MessagingException expected.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, Matchers.instanceOf(IllegalArgumentException.class));
|
||||
assertEquals("'resultArg[0]' can be an instance of 'NodeMapper<?>' or " +
|
||||
"one of supported String constants: [string, boolean, number, node, node_list, document_list]", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3140Transformer() {
|
||||
Message<?> message = MessageBuilder.withPayload("<person name='John Doe' age='42' married='true'/>")
|
||||
.setHeader("xpath", "/person/@age")
|
||||
.build();
|
||||
|
||||
this.xpathTransformerInput.send(message);
|
||||
|
||||
Message<?> receive = this.channelA.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("42-mapped", receive.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3140Filter() {
|
||||
this.xpathFilterInput.send(new GenericMessage<Object>("<name>outputOne</name>"));
|
||||
this.xpathFilterInput.send(new GenericMessage<Object>("<other>outputOne</other>"));
|
||||
|
||||
Message<?> receive = this.channelA.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("<name>outputOne</name>", receive.getPayload());
|
||||
|
||||
receive = this.channelZ.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("<other>outputOne</other>", receive.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3140Splitter() {
|
||||
StringSourceFactory stringSourceFactory = new StringSourceFactory();
|
||||
this.xpathSplitterInput.send(new GenericMessage<Object>("<books><book>book1</book><book>book2</book></books>"));
|
||||
|
||||
Message<?> receive = this.channelA.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertThat(stringSourceFactory.createSource(receive.getPayload()).toString(), Matchers.containsString("<book>book1</book>"));
|
||||
|
||||
receive = this.channelA.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertThat(stringSourceFactory.createSource(receive.getPayload()).toString(), Matchers.containsString("<book>book2</book>"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInt3140Router() {
|
||||
this.xpathRouterInput.send(new GenericMessage<Object>("<name>A</name>"));
|
||||
this.xpathRouterInput.send(new GenericMessage<Object>("<name>B</name>"));
|
||||
this.xpathRouterInput.send(new GenericMessage<Object>("<name>X</name>"));
|
||||
|
||||
Message<?> receive = this.channelA.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("<name>A</name>", receive.getPayload());
|
||||
|
||||
receive = this.channelB.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("<name>B</name>", receive.getPayload());
|
||||
|
||||
receive = this.channelZ.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("<name>X</name>", receive.getPayload());
|
||||
}
|
||||
|
||||
|
||||
public static class TestNodeMapper implements NodeMapper<String> {
|
||||
|
||||
@Override
|
||||
public String mapNode(Node node, int nodeNum) throws DOMException {
|
||||
return node.getTextContent() + "-mapped";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -176,13 +176,11 @@
|
||||
</note>
|
||||
For more information regarding JSON see 'JSON Transformers' in <xref linkend="transformer"/>.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis role="bold">#xpath</emphasis> - to evaluate an 'xpath' on some provided object.
|
||||
For more information regarding xml and xpath see <xref linkend="xml"/>.
|
||||
</listitem>
|
||||
<!--<listitem>
|
||||
<emphasis>#xpath</emphasis> - TBD
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#request</emphasis> - TBD
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#auth</emphasis> - TBD
|
||||
</listitem>-->
|
||||
</itemizedlist>
|
||||
|
||||
@@ -361,6 +361,13 @@ public class Foo {
|
||||
In addition to JSON Transformers, Spring Integration provides a built-in <emphasis>#jsonPath</emphasis>
|
||||
SpEL function for use in expressions. For more information see <xref linkend="spel"/>.
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">#xpath SpEL Function</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
Since version <emphasis>3.0</emphasis>, Spring Integration also provides a built-in <emphasis>#xpath</emphasis>
|
||||
SpEL function for use in expressions. For more information see <xref linkend="xpath-spel-function"/>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="transformer-annotation">
|
||||
|
||||
@@ -117,8 +117,9 @@
|
||||
<title>SpEL Functions Support</title>
|
||||
<para>
|
||||
To customize the SpEL <interfacename>EvaluationContext</interfacename> with static
|
||||
<classname>Method</classname> functions the new <code><spel-function/></code>
|
||||
component is introduced. For more information see <xref linkend="spel-functions" />.
|
||||
<classname>Method</classname> functions, the new <code><spel-function/></code>
|
||||
component is introduced. Two built-in functions are also provided (<code>#jsonPath</code>
|
||||
and <code>#xpath</code>). For more information see <xref linkend="spel-functions" />.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-spel-property-accessors">
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
<listitem>
|
||||
<para><emphasis><link linkend='xml-xpath-filter'>XPath Filter</link></emphasis></para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><emphasis><link linkend='xpath-spel-function'>#xpath SpEL Function</link></emphasis></para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><emphasis><link linkend='xml-validating-filter'>Validating Filter</link></emphasis></para>
|
||||
</listitem>
|
||||
@@ -1253,6 +1256,39 @@
|
||||
</calloutlist></para>
|
||||
</section>
|
||||
|
||||
<section id="xpath-spel-function">
|
||||
<title>#xpath SpEL Function</title>
|
||||
<para>
|
||||
Spring Integration, since version <emphasis>3.0</emphasis>, provides the <code>#xpath</code>
|
||||
built-in SpEL function, which invokes the static method <code>XPathUtils.evaluate(...)</code>.
|
||||
This method delegates to an <interfacename>org.springframework.xml.xpath.XPathExpression</interfacename>.
|
||||
The following shows some usage examples:
|
||||
<programlisting language="xml"><![CDATA[<transformer expression="#xpath(payload, '/name')"/>
|
||||
|
||||
<filter expression="#xpath(payload, headers.xpath, 'boolean')"/>
|
||||
|
||||
<splitter expression="#xpath(payload, '//book', 'document_list')"/>
|
||||
|
||||
<router expression="#xpath(payload, '/person/@age', 'number')">
|
||||
<mapping channel="output1" value="16"/>
|
||||
<mapping channel="output2" value="45"/>
|
||||
</router>]]></programlisting>
|
||||
<code>#xpath</code> also supports a third optional parameter for converting the result of the xpath evaluation.
|
||||
It can be
|
||||
one of the String constants <code>'string'</code>, <code>'boolean'</code>, <code>'number'</code>,
|
||||
<code>'node'</code>, <code>'node_list'</code> and <code>'document_list'</code> or an
|
||||
<interfacename>org.springframework.xml.xpath.NodeMapper</interfacename> instance.
|
||||
By default the <code>#xpath</code> SpEL function returns a String representation of the xpath evaluation.
|
||||
</para>
|
||||
<note>
|
||||
To enable the <code>#xpath</code> SpEL function, simply add the <code>spring-integration-xml.jar</code>
|
||||
to the CLASSPATH; there is no need to declare any component(s) from the Spring Integration Xml Namespace.
|
||||
</note>
|
||||
<para>
|
||||
For more information see <xref linkend="spel"/>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="xml-validating-filter">
|
||||
<title>XML Validating Filter</title>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user