This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch xp in repository https://gitbox.apache.org/repos/asf/camel.git
commit ba948d2a26b9ff5b42d42ba4a6f673aa0b590220 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Fri Nov 24 15:41:29 2023 +0100 CAMEL-20151: camel-xpath - Add xpath as function to simple for basic templating --- .../camel-xpath/src/main/docs/xpath-language.adoc | 57 ++++++++++++++++++ .../modules/languages/pages/simple-language.adoc | 3 + .../simple/ast/SimpleFunctionExpression.java | 9 +++ .../camel/language/SimpleXPathTransformTest.java | 69 ++++++++++++++++++++++ 4 files changed, 138 insertions(+) diff --git a/components/camel-xpath/src/main/docs/xpath-language.adoc b/components/camel-xpath/src/main/docs/xpath-language.adoc index da7706b8b94..43f736a571b 100644 --- a/components/camel-xpath/src/main/docs/xpath-language.adoc +++ b/components/camel-xpath/src/main/docs/xpath-language.adoc @@ -513,6 +513,63 @@ eg to refer to a file on the classpath you can do: .setHeader("myHeader").xpath("resource:classpath:myxpath.txt", String.class) ---- +== Transforming a XML message + +For basic XML transformation where you have a fixed structure you can represent with a combination of using +Camel simple and XPath language as: + +Given this XML body + +[source,xml] +---- +<order id="123"> + <item>Brake</item> + <first>scott</first> + <last>jackson</last> + <address> + <co>sweden</co> + <zip>12345</zip> + </address> +</order> +---- + +Which you want to transform to a smaller structure: + +[source,xml] +---- +<user> + <rool>123</rool> + <country>sweden</country> + <fullname>scott</fullname> +</user> +---- + +Then you can use simple as template and XPath to grab the content from the message payload, as shown in the route snippet below: + +[source,java] +---- +from("direct:start") + .transform().simple(""" + <user> + <rool>${xpath(/order/@id)}</rool> + <country>${xpath(/order/address/co/text())}</country> + <fullname>${xpath(/order/first/text())}</fullname> + </user>""") + .to("mock:result"); +---- + +Notice how we use `${xpath(exp}` syntax in the simple template to use xpath that will be evaluated on the message body, +to extract the content to be used in the output (see previous for output). + +Since the simple language can output anything you can also use this to output in plain text or JSon etc. + +[source,java] +---- +from("direct:start") + .transform().simple("The order ${xpath(/order/@id)} is being shipped to ${xpath(/order/address/co/text())}") + .to("mock:result"); +---- + == Dependencies To use XPath in your camel routes you need to add the dependency on diff --git a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc index b2f43ee25c2..9d9452bb9b1 100644 --- a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc +++ b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc @@ -255,6 +255,9 @@ where the ID is _myGenerator_. |jq(exp) | Object | When working with JSon data, then this allows to use the JQ language for example to extract data from the message body (in JSon format). This requires having camel-jq JAR on the classpath. +|xpath(exp) | Object | When working with XML data, then this allows to use the XPath language +for example to extract data from the message body (in XML format). This requires having camel-xpath JAR on the classpath. + |======================================================================= == OGNL expression support diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java index edf10b84a0c..d0f93f30cce 100644 --- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java @@ -462,6 +462,15 @@ public class SimpleFunctionExpression extends LiteralExpression { exp = StringHelper.removeQuotes(exp); return ExpressionBuilder.languageExpression("jq", exp); } + remainder = ifStartsWithReturnRemainder("xpath(", function); + if (remainder != null) { + String exp = StringHelper.beforeLast(remainder, ")"); + if (exp == null) { + throw new SimpleParserException("Valid syntax: ${xpath(exp)} was: " + function, token.getIndex()); + } + exp = StringHelper.removeQuotes(exp); + return ExpressionBuilder.languageExpression("xpath", exp); + } return null; } diff --git a/core/camel-core/src/test/java/org/apache/camel/language/SimpleXPathTransformTest.java b/core/camel-core/src/test/java/org/apache/camel/language/SimpleXPathTransformTest.java new file mode 100644 index 00000000000..dbba17e0d76 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/language/SimpleXPathTransformTest.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.language; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.jupiter.api.Test; + +public class SimpleXPathTransformTest extends ContextTestSupport { + + private static String EXPECTED = """ + <user> + <rool>123</rool> + <country>sweden</country> + <fullname>scott</fullname> + </user>"""; + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:start") + .transform().simple(""" + <user> + <rool>${xpath(/order/@id)}</rool> + <country>${xpath(/order/address/co/text())}</country> + <fullname>${xpath(/order/first/text())}</fullname> + </user>""") + .to("mock:result"); + } + }; + } + + @Test + public void testTransform() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived(EXPECTED); + + template.sendBody("direct:start", """ + <order id="123"> + <item>Brake</item> + <first>scott</first> + <last>jackson</last> + <address> + <co>sweden</co> + <zip>12345</zip> + </address> + </order> + """); + + MockEndpoint.assertIsSatisfied(context); + } + +}