This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 0e479de49b5 CAMEL-21420: XML and YAML DSL should better support
languages with namespaces such as xpath when parsing and writing outputs
(#16219)
0e479de49b5 is described below
commit 0e479de49b5024311c32d6d83a4920d55856364b
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 11 12:11:08 2024 +0100
CAMEL-21420: XML and YAML DSL should better support languages with
namespaces such as xpath when parsing and writing outputs (#16219)
* CAMEL-21420 - Failing tests
* CAMEL-21420: camel-xml-io: Parser should handle default Camel namespace
better.
* CAMEL-21420: camel-yaml-io - Should support writing namespace as output
for xpath and other languages that can do namespaces
* CAMEL-21420: camel-xml-io: Parser should handle default Camel namespace
better.
* CAMEL-21420: camel-xml-io: Parser should handle default Camel namespace
better.
---------
Co-authored-by: Adriano Machado <[email protected]>
---
.../model/language/NamespaceAwareExpression.java | 5 +
core/camel-xml-io/pom.xml | 11 ++
.../java/org/apache/camel/xml/in/BaseParser.java | 20 ++-
.../java/org/apache/camel/xml/out/BaseWriter.java | 10 +-
.../org/apache/camel/xml/out/ModelWriterTest.java | 171 +++++++++++++++++++++
.../src/test/resources/beansWithFactoryMethod.xml | 1 +
.../src/test/resources/resequencerStream.xml | 2 +-
.../test/resources/routeXPathAndXmlNamespaces.xml | 43 ++++++
.../src/test/resources/routingSlip.xml | 2 +-
.../src/test/resources/routingSlipHeaderSet.xml | 2 +-
core/camel-yaml-io/pom.xml | 10 ++
.../org/apache/camel/yaml/out/ModelWriter.java | 2 +-
.../java/org/apache/camel/yaml/io/YamlWriter.java | 38 ++++-
.../apache/camel/yaml/out/XPathNamespacesTest.java | 118 ++++++++++++++
.../src/main/resources/velocity/model-writer.vm | 3 +-
15 files changed, 424 insertions(+), 14 deletions(-)
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/model/language/NamespaceAwareExpression.java
b/core/camel-core-model/src/main/java/org/apache/camel/model/language/NamespaceAwareExpression.java
index 834158bb6ef..891059bd175 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/model/language/NamespaceAwareExpression.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/model/language/NamespaceAwareExpression.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.model.language;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -80,6 +81,10 @@ public abstract class NamespaceAwareExpression extends
SingleInputTypedExpressio
}
public List<PropertyDefinition> getNamespace() {
+ if (namespace == null && namespaces != null && !namespaces.isEmpty()) {
+ namespace = new ArrayList<>();
+ namespaces.forEach((k, v) -> namespace.add(new
PropertyDefinition(k, v)));
+ }
return namespace;
}
diff --git a/core/camel-xml-io/pom.xml b/core/camel-xml-io/pom.xml
index 152afbf8b48..f7e5e363d99 100644
--- a/core/camel-xml-io/pom.xml
+++ b/core/camel-xml-io/pom.xml
@@ -54,6 +54,17 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.xmlunit</groupId>
+ <artifactId>xmlunit-assertj3</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>${commons-io-version}</version>
+ <scope>test</scope>
+ </dependency>
<!-- logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
diff --git
a/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
b/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
index 4b338253f06..ba2620e2742 100644
--- a/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
+++ b/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
@@ -53,6 +53,8 @@ import org.apache.camel.xml.io.XmlPullParserLocationException;
public class BaseParser {
+ public static final String DEFAULT_NAMESPACE =
"http://camel.apache.org/schema/spring";
+
protected final MXParser parser;
protected String namespace;
protected final Set<String> secondaryNamespaces = new HashSet<>();
@@ -80,14 +82,14 @@ public class BaseParser {
this.parser = new MXParser();
this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
this.parser.setInput(input, null);
- this.namespace = namespace != null ? namespace : "";
+ this.namespace = namespace != null && !namespace.isEmpty() ? namespace
: DEFAULT_NAMESPACE;
}
public BaseParser(Reader reader, String namespace) throws IOException,
XmlPullParserException {
this.parser = new MXParser();
this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
this.parser.setInput(reader);
- this.namespace = namespace != null ? namespace : "";
+ this.namespace = namespace != null && !namespace.isEmpty() ? namespace
: DEFAULT_NAMESPACE;
}
public void addSecondaryNamespace(String namespace) {
@@ -528,9 +530,17 @@ public class BaseParser {
if (Objects.equals(ns, namespace)) {
return true;
}
- for (String second : secondaryNamespaces) {
- if (Objects.equals(ns, second)) {
- return true;
+ if (DEFAULT_NAMESPACE.equals(ns) && namespace.isEmpty()) {
+ return true;
+ }
+ if (DEFAULT_NAMESPACE.equals(namespace) && ns.isEmpty()) {
+ return true;
+ }
+ if (secondaryNamespaces != null) {
+ for (String second : secondaryNamespaces) {
+ if (Objects.equals(ns, second)) {
+ return true;
+ }
}
}
return false;
diff --git
a/core/camel-xml-io/src/main/java/org/apache/camel/xml/out/BaseWriter.java
b/core/camel-xml-io/src/main/java/org/apache/camel/xml/out/BaseWriter.java
index a8c4b779ce7..cfd9db0434f 100644
--- a/core/camel-xml-io/src/main/java/org/apache/camel/xml/out/BaseWriter.java
+++ b/core/camel-xml-io/src/main/java/org/apache/camel/xml/out/BaseWriter.java
@@ -33,14 +33,19 @@ import org.apache.camel.xml.io.XMLWriter;
public class BaseWriter {
+ public static final String DEFAULT_NAMESPACE =
"http://camel.apache.org/schema/spring";
+
protected final XMLWriter writer;
protected final Deque<String> namespacesStack = new LinkedList<>();
protected boolean namespaceWritten;
+ protected boolean skipCustomId = true;
public BaseWriter(Writer writer, String namespace) throws IOException {
this.writer = new XMLWriter(writer);
- if (namespace != null) {
+ if (namespace != null && !namespace.isEmpty()) {
this.namespacesStack.push(namespace);
+ } else {
+ this.namespacesStack.push(DEFAULT_NAMESPACE);
}
}
@@ -96,6 +101,9 @@ public class BaseWriter {
}
protected void attribute(String name, Object value) throws IOException {
+ if (skipCustomId && "customId".equals(name)) {
+ return;
+ }
if (value != null) {
writer.addAttribute(name, value.toString());
}
diff --git
a/core/camel-xml-io/src/test/java/org/apache/camel/xml/out/ModelWriterTest.java
b/core/camel-xml-io/src/test/java/org/apache/camel/xml/out/ModelWriterTest.java
index 66a1b732ad5..afdafe6dce7 100644
---
a/core/camel-xml-io/src/test/java/org/apache/camel/xml/out/ModelWriterTest.java
+++
b/core/camel-xml-io/src/test/java/org/apache/camel/xml/out/ModelWriterTest.java
@@ -16,6 +16,8 @@
*/
package org.apache.camel.xml.out;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
@@ -44,13 +46,19 @@ import org.apache.camel.model.app.BeansDefinition;
import org.apache.camel.model.rest.RestsDefinition;
import org.apache.camel.util.StringHelper;
import org.apache.camel.xml.in.ModelParser;
+import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import org.xmlunit.assertj3.XmlAssert;
+import org.xmlunit.diff.DefaultNodeMatcher;
+import org.xmlunit.diff.ElementSelectors;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.fail;
@@ -73,6 +81,42 @@ public class ModelWriterTest {
}
}
+ @ParameterizedTest
+ @MethodSource("routes")
+ @DisplayName("Test xml roundtrip for <routes>, then compare generated XML")
+ void testRoutesWithDiff(String xml, String ns) throws Exception {
+ String original;
+ try (InputStream is =
getClass().getClassLoader().getResourceAsStream(xml);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ assertNotNull(is);
+ IOUtils.copy(is, baos);
+ original = baos.toString();
+ }
+
+ assertThat(original).isNotBlank();
+ RoutesDefinition expected
+ = new ModelParser(new
ByteArrayInputStream(original.getBytes()),
NAMESPACE).parseRoutesDefinition().get();
+ StringWriter sw = new StringWriter();
+ new ModelWriter(sw, ns).writeRoutesDefinition(expected);
+ String generatedXml = sw.toString();
+ assertThat(generatedXml).isNotBlank();
+
+ XmlAssert.assertThat(generatedXml)
+ .and(original)
+ .withNodeMatcher(new
DefaultNodeMatcher(ElementSelectors.byNameAndText))
+ .withNodeFilter(node -> {
+ // skip comparing namespace as original have namespaces
scattered in other places than inside <xpath>
+ if ("namespace".equals(node.getLocalName())) {
+ return false;
+ }
+ return true;
+ })
+ .ignoreWhitespace()
+ .ignoreElementContentWhitespace()
+ .ignoreComments()
+ .areSimilar();
+ }
+
@ParameterizedTest
@MethodSource("rests")
@DisplayName("Test xml roundtrip for <rests>")
@@ -86,6 +130,44 @@ public class ModelWriterTest {
}
}
+ @ParameterizedTest
+ @MethodSource("rests")
+ @DisplayName("Test xml roundtrip for <rests>, then compare generated XML")
+ void testRestsWithDiff(String xml, String ns) throws Exception {
+ String original;
+ try (InputStream is =
getClass().getClassLoader().getResourceAsStream(xml);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ assertNotNull(is);
+ IOUtils.copy(is, baos);
+ original = baos.toString();
+ }
+
+ RestsDefinition expected
+ = new ModelParser(new
ByteArrayInputStream(original.getBytes()),
NAMESPACE).parseRestsDefinition().get();
+ StringWriter sw = new StringWriter();
+ new ModelWriter(sw, ns).writeRestsDefinition(expected);
+ String generatedXml = sw.toString();
+ assertThat(generatedXml).isNotBlank();
+
+ XmlAssert.assertThat(generatedXml)
+ .and(original)
+ .withNodeMatcher(new
DefaultNodeMatcher(ElementSelectors.byNameAndText))
+ .withAttributeFilter(attr -> {
+ // skip default values for rest-dsl header params
+ if ("header".equals(attr.getOwnerElement().getTagName())
&& "arrayType".equals(attr.getName())) {
+ return false;
+ }
+ if ("header".equals(attr.getOwnerElement().getTagName())
&& "collectionFormat".equals(attr.getName())) {
+ return false;
+ }
+ return true;
+ })
+ .ignoreWhitespace()
+ .ignoreElementContentWhitespace()
+ .ignoreComments()
+ .areSimilar();
+ }
+
@ParameterizedTest
@MethodSource("routeTemplates")
@DisplayName("Test xml roundtrip for <routeTemplates>")
@@ -100,6 +182,34 @@ public class ModelWriterTest {
}
}
+ @ParameterizedTest
+ @MethodSource("routeTemplates")
+ @DisplayName("Test xml roundtrip for <routeTemplates> then compare
generated XML")
+ void testRouteTemplatesWithDiff(String xml, String ns) throws Exception {
+ String original;
+ try (InputStream is =
getClass().getClassLoader().getResourceAsStream(xml);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ assertNotNull(is);
+ IOUtils.copy(is, baos);
+ original = baos.toString();
+ }
+
+ RouteTemplatesDefinition expected = new ModelParser(new
ByteArrayInputStream(original.getBytes()), NAMESPACE)
+ .parseRouteTemplatesDefinition().get();
+ StringWriter sw = new StringWriter();
+ new ModelWriter(sw, ns).writeRouteTemplatesDefinition(expected);
+ String generatedXml = sw.toString();
+ assertThat(generatedXml).isNotBlank();
+
+ XmlAssert.assertThat(generatedXml)
+ .and(original)
+ .withNodeMatcher(new
DefaultNodeMatcher(ElementSelectors.byNameAndText))
+ .ignoreWhitespace()
+ .ignoreElementContentWhitespace()
+ .ignoreComments()
+ .areSimilar();
+ }
+
@ParameterizedTest
@MethodSource("templatedRoutes")
@DisplayName("Test xml roundtrip for <templatedRoutes>")
@@ -114,6 +224,33 @@ public class ModelWriterTest {
}
}
+ @ParameterizedTest
+ @MethodSource("templatedRoutes")
+ @DisplayName("Test xml roundtrip for <templatedRoutes> then compare
generated XML")
+ void testTemplatedRoutesWithDiff(String xml, String ns) throws Exception {
+ String original;
+ try (InputStream is =
getClass().getClassLoader().getResourceAsStream(xml);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ assertNotNull(is);
+ IOUtils.copy(is, baos);
+ original = baos.toString();
+ }
+ TemplatedRoutesDefinition expected = new ModelParser(new
ByteArrayInputStream(original.getBytes()), NAMESPACE)
+ .parseTemplatedRoutesDefinition().get();
+ StringWriter sw = new StringWriter();
+ new ModelWriter(sw, ns).writeTemplatedRoutesDefinition(expected);
+ String generatedXml = sw.toString();
+ assertThat(generatedXml).isNotBlank();
+
+ XmlAssert.assertThat(generatedXml)
+ .and(original)
+ .withNodeMatcher(new
DefaultNodeMatcher(ElementSelectors.byNameAndText))
+ .ignoreWhitespace()
+ .ignoreElementContentWhitespace()
+ .ignoreComments()
+ .areSimilar();
+ }
+
@ParameterizedTest
@MethodSource("beans")
@DisplayName("Test xml roundtrip for <beans>")
@@ -128,6 +265,40 @@ public class ModelWriterTest {
}
}
+ @ParameterizedTest
+ @MethodSource("beans")
+ @DisplayName("Test xml roundtrip for <beans> then compare generated XML")
+ void testBeansWithDiff(String xml, String ns) throws Exception {
+ String original;
+ try (InputStream is =
getClass().getClassLoader().getResourceAsStream(xml);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ assertNotNull(is);
+ IOUtils.copy(is, baos);
+ original = baos.toString();
+ }
+ BeansDefinition expected
+ = new ModelParser(new
ByteArrayInputStream(original.getBytes()),
NAMESPACE).parseBeansDefinition().get();
+ StringWriter sw = new StringWriter();
+ new ModelWriter(sw, ns).writeBeansDefinition(expected);
+ String generatedXml = sw.toString();
+ assertThat(generatedXml).isNotBlank();
+
+ XmlAssert.assertThat(generatedXml)
+ .and(original)
+ .withNodeMatcher(new
DefaultNodeMatcher(ElementSelectors.byNameAndText))
+ .withAttributeFilter(attr -> {
+ // bean constructor index is optional
+ if
("constructor".equals(attr.getOwnerElement().getTagName()) &&
"index".equals(attr.getName())) {
+ return false;
+ }
+ return true;
+ })
+ .ignoreWhitespace()
+ .ignoreElementContentWhitespace()
+ .ignoreComments()
+ .areSimilar();
+ }
+
private static Stream<Arguments> routes() {
return definitions("routes");
}
diff --git a/core/camel-xml-io/src/test/resources/beansWithFactoryMethod.xml
b/core/camel-xml-io/src/test/resources/beansWithFactoryMethod.xml
index 9065010d4c4..20c1afa8242 100644
--- a/core/camel-xml-io/src/test/resources/beansWithFactoryMethod.xml
+++ b/core/camel-xml-io/src/test/resources/beansWithFactoryMethod.xml
@@ -22,6 +22,7 @@ xsi:schemaLocation="http://camel.apache.org/schema/spring
file:/data/sources/git
<bean name="b1" type="org.apache.camel.xml.in.ModelParserTest.MyBean"
factoryMethod="createMyBean">
<constructors>
+ <!-- index is optional -->
<constructor value="c1" />
<constructor value="c2" />
</constructors>
diff --git a/core/camel-xml-io/src/test/resources/resequencerStream.xml
b/core/camel-xml-io/src/test/resources/resequencerStream.xml
index 7905dd942c0..09deea289c6 100644
--- a/core/camel-xml-io/src/test/resources/resequencerStream.xml
+++ b/core/camel-xml-io/src/test/resources/resequencerStream.xml
@@ -21,7 +21,7 @@
<route>
<from uri="direct:start" />
<resequence>
- <streamConfig timeout="2000"/> <!-- Use default capacity -->
+ <streamConfig capacity="1000" timeout="2000"/>
<simple>in.header.seqnum</simple>
<to uri="mock:result" />
</resequence>
diff --git
a/core/camel-xml-io/src/test/resources/routeXPathAndXmlNamespaces.xml
b/core/camel-xml-io/src/test/resources/routeXPathAndXmlNamespaces.xml
new file mode 100644
index 00000000000..74cdae56b39
--- /dev/null
+++ b/core/camel-xml-io/src/test/resources/routeXPathAndXmlNamespaces.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<routes xmlns="http://camel.apache.org/schema/spring"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:routes-ns-def="http://www.example.com/schema"
+ xsi:schemaLocation="http://camel.apache.org/schema/spring
https://camel.apache.org/schema/spring/camel-spring.xsd">
+
+ <route id="direct:route-with-xpath-expression-custom-namespace"
+ xmlns:route-ns-def="http://www.example.com/schema">
+
+ <from uri="direct:route-with-xpath-expression-custom-namespace"/>
+
+ <setProperty name="child-expression-namespace-from-routes">
+ <xpath saxon="true"
resultType="java.lang.String">/routes-ns-def:parent/routes-ns-def:child</xpath>
+ </setProperty>
+
+ <setProperty name="child-expression-namespace-from-route">
+ <xpath saxon="true"
resultType="java.lang.String">/route-ns-def:parent/route-ns-def:child</xpath>
+ </setProperty>
+
+ <setProperty name="child-expression-namespace-from-xpath">
+ <xpath saxon="true" resultType="java.lang.String"
xmlns:expression-ns-def="http://www.example.com/schema">/expression-ns-def:parent/expression-ns-def:child</xpath>
+ </setProperty>
+ </route>
+
+</routes>
diff --git a/core/camel-xml-io/src/test/resources/routingSlip.xml
b/core/camel-xml-io/src/test/resources/routingSlip.xml
index 23c6d2fe414..74ceb723762 100644
--- a/core/camel-xml-io/src/test/resources/routingSlip.xml
+++ b/core/camel-xml-io/src/test/resources/routingSlip.xml
@@ -20,7 +20,7 @@
<routes id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="seda:a"/>
- <routingSlip>
+ <routingSlip uriDelimiter=",">
<header>destinations</header>
</routingSlip>
</route>
diff --git a/core/camel-xml-io/src/test/resources/routingSlipHeaderSet.xml
b/core/camel-xml-io/src/test/resources/routingSlipHeaderSet.xml
index 1a60dc66dbb..b0c21431473 100644
--- a/core/camel-xml-io/src/test/resources/routingSlipHeaderSet.xml
+++ b/core/camel-xml-io/src/test/resources/routingSlipHeaderSet.xml
@@ -20,7 +20,7 @@
<routes id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="seda:a"/>
- <routingSlip>
+ <routingSlip uriDelimiter=",">
<header>theRoutingSlipHeader</header>
</routingSlip>
</route>
diff --git a/core/camel-yaml-io/pom.xml b/core/camel-yaml-io/pom.xml
index 058c56f5b63..3f3176dbc21 100644
--- a/core/camel-yaml-io/pom.xml
+++ b/core/camel-yaml-io/pom.xml
@@ -66,6 +66,16 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.xmlunit</groupId>
+ <artifactId>xmlunit-assertj3</artifactId>
+ <scope>test</scope>
+ </dependency>
<!-- logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
diff --git
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index fcfbc2c9ad8..f2361c77925 100644
---
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -41,7 +41,7 @@ import org.apache.camel.model.tokenizer.*;
import org.apache.camel.model.transformer.*;
import org.apache.camel.model.validator.*;
-@Generated("org.apache.camel.maven.packaging.XmlModelWriterGeneratorMojo")
+@Generated("org.apache.camel.maven.packaging.YamlModelWriterGeneratorMojo")
public class ModelWriter extends BaseWriter {
public ModelWriter(Writer writer, String namespace) throws IOException {
diff --git
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/io/YamlWriter.java
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/io/YamlWriter.java
index 7e7eb3121b2..f121a8d03f9 100644
--- a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/io/YamlWriter.java
+++ b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/io/YamlWriter.java
@@ -21,6 +21,7 @@ import java.io.Writer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
@@ -94,6 +95,14 @@ public class YamlWriter extends ServiceSupport implements
CamelContextAware {
}
}
+ private EipModel lookupEipModel(String name) {
+ // namespace is using the property model
+ if ("namespace".equals(name)) {
+ name = "property";
+ }
+ return catalog.eipModel(name);
+ }
+
public void setUriAsParameters(boolean uriAsParameters) {
this.uriAsParameters = uriAsParameters;
}
@@ -105,9 +114,9 @@ public class YamlWriter extends ServiceSupport implements
CamelContextAware {
return;
}
- EipModel model = catalog.eipModel(name);
+ EipModel model = lookupEipModel(name);
if (model == null) {
- // not an EIP model
+ // not an EIP model or namespace
return;
}
@@ -137,12 +146,35 @@ public class YamlWriter extends ServiceSupport implements
CamelContextAware {
return;
}
- EipModel model = catalog.eipModel(name);
+ EipModel model = lookupEipModel(name);
if (model == null) {
// not an EIP model
return;
}
+ // special for namespace
+ if ("namespace".equals(name)) {
+ EipModel last = models.isEmpty() ? null : models.peek();
+ if (!models.isEmpty()) {
+ models.pop();
+ }
+ EipModel parent = models.isEmpty() ? null : models.peek();
+ if (parent != null) {
+ Map<String, String> map = (Map<String, String>)
parent.getMetadata().get("namespace");
+ if (map == null) {
+ map = new LinkedHashMap<>();
+ parent.getMetadata().put("namespace", map);
+ }
+ String key = (String) last.getMetadata().get("key");
+ String value = (String) last.getMetadata().get("value");
+ // skip xsi namespace
+ if (key != null && !"xsi".equals(key) && value != null) {
+ map.put(key, value);
+ }
+ }
+ return;
+ }
+
EipModel last = models.isEmpty() ? null : models.peek();
if (last != null && isLanguage(last)) {
if (!models.isEmpty()) {
diff --git
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/XPathNamespacesTest.java
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/XPathNamespacesTest.java
new file mode 100644
index 00000000000..a5ad9e372ff
--- /dev/null
+++
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/XPathNamespacesTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.yaml.out;
+
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+import java.util.Optional;
+
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.xml.in.ModelParser;
+import org.assertj.core.api.InstanceOfAssertFactories;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class XPathNamespacesTest {
+
+ @Test
+ void testNamespace() throws Exception {
+ try (ByteArrayInputStream is = new
ByteArrayInputStream(XML.getBytes(Charset.defaultCharset()))) {
+ Optional<RoutesDefinition> routesDefinition = new ModelParser(is,
XmlToYamlTest.NAMESPACE).parseRoutesDefinition();
+ assertThat(routesDefinition).isPresent()
+
.get(InstanceOfAssertFactories.type(RoutesDefinition.class))
+ .extracting(RoutesDefinition::getRoutes,
InstanceOfAssertFactories.list(RouteDefinition.class))
+ .singleElement()
+ .extracting(RouteDefinition::getOutputs,
InstanceOfAssertFactories.list(ProcessorDefinition.class))
+ .hasSize(3);
+
+ StringWriter sw = new StringWriter();
+ new
org.apache.camel.yaml.out.ModelWriter(sw).writeRoutesDefinition(routesDefinition.get());
+
+ assertThat(sw).hasToString(EXPECTED_YAML);
+ }
+ }
+
+ //language=XML
+ private static final String XML
+ = """
+ <routes xmlns="http://camel.apache.org/schema/spring"
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:routes-ns-def="http://www.example.com/schema"
+
xsi:schemaLocation="http://camel.apache.org/schema/spring
https://camel.apache.org/schema/spring/camel-spring.xsd">
+
+ <route
id="direct:route-with-xpath-expression-custom-namespace"
+
xmlns:route-ns-def="http://www.example.com/schema">
+
+ <from
uri="direct:route-with-xpath-expression-custom-namespace"/>
+
+ <setProperty
name="child-expression-namespace-from-routes">
+ <xpath saxon="true"
resultType="java.lang.String">/routes-ns-def:parent/routes-ns-def:child</xpath>
+ </setProperty>
+
+ <setProperty
name="child-expression-namespace-from-route">
+ <xpath saxon="true"
resultType="java.lang.String">/route-ns-def:parent/route-ns-def:child</xpath>
+ </setProperty>
+
+ <setProperty
name="child-expression-namespace-from-xpath">
+ <xpath saxon="true"
resultType="java.lang.String"
xmlns:expression-ns-def="http://www.example.com/schema">/expression-ns-def:parent/expression-ns-def:child</xpath>
+ </setProperty>
+ </route>
+
+ </routes>
+ """;
+
+ //language=yaml
+ private static final String EXPECTED_YAML = """
+ - route:
+ id: direct:route-with-xpath-expression-custom-namespace
+ from:
+ uri: direct:route-with-xpath-expression-custom-namespace
+ steps:
+ - setProperty:
+ name: child-expression-namespace-from-routes
+ xpath:
+ resultType: java.lang.String
+ saxon: "true"
+ expression: /routes-ns-def:parent/routes-ns-def:child
+ namespace:
+ routes-ns-def: http://www.example.com/schema
+ route-ns-def: http://www.example.com/schema
+ - setProperty:
+ name: child-expression-namespace-from-route
+ xpath:
+ resultType: java.lang.String
+ saxon: "true"
+ expression: /route-ns-def:parent/route-ns-def:child
+ namespace:
+ routes-ns-def: http://www.example.com/schema
+ route-ns-def: http://www.example.com/schema
+ - setProperty:
+ name: child-expression-namespace-from-xpath
+ xpath:
+ resultType: java.lang.String
+ saxon: "true"
+ expression:
/expression-ns-def:parent/expression-ns-def:child
+ namespace:
+ routes-ns-def: http://www.example.com/schema
+ route-ns-def: http://www.example.com/schema
+ expression-ns-def: http://www.example.com/schema
+ """;
+}
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-writer.vm
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-writer.vm
index ee132961438..60e4b647916 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-writer.vm
+++
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-writer.vm
@@ -44,6 +44,7 @@ import java.util.List;
import javax.annotation.processing.Generated;
#set( $pkgs = $mojo.newTreeSet() )
+#set( $mojoClassName = $mojo.getClass().getName() )
#foreach( $clazz in $model )
#set( $foo = $pkgs.add($clazz.getPackageName()) )
#end
@@ -51,7 +52,7 @@ import javax.annotation.processing.Generated;
import ${pkg}.*;
#end
-@Generated("org.apache.camel.maven.packaging.XmlModelWriterGeneratorMojo")
+@Generated("${mojoClassName}")
public class ModelWriter extends BaseWriter {
public ModelWriter(Writer writer, String namespace) throws IOException {