This is an automated email from the ASF dual-hosted git repository.
Croway 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 9a1f3883f947 Add namespacesRef to XPath/XQuery/XTokenize expressions
(#25843)
9a1f3883f947 is described below
commit 9a1f3883f947936e56a1f7385d0790ad8be62dd4
Author: Marcin Więckowski <[email protected]>
AuthorDate: Fri Aug 28 16:51:55 2026 +0200
Add namespacesRef to XPath/XQuery/XTokenize expressions (#25843)
* Add namespacesRef to XPath/XQuery/XTokenize expressions
Co-authored-by: Claude Opus 5 <[email protected]>
* NamespacesRef tests for XQuery and XML Tokenize
Co-authored-by: Claude Opus 5 <[email protected]>
---------
Co-authored-by: Claude Opus 5 <[email protected]>
---
.../apache/camel/catalog/docs/xpath-language.adoc | 80 +++++++++++++++++
.../apache/camel/catalog/docs/xquery-language.adoc | 64 ++++++++++++++
.../camel/catalog/docs/xtokenize-language.adoc | 61 +++++++++++++
.../org/apache/camel/catalog/languages/xpath.json | 7 +-
.../org/apache/camel/catalog/languages/xquery.json | 7 +-
.../apache/camel/catalog/languages/xtokenize.json | 7 +-
.../org/apache/camel/catalog/models/xpath.json | 7 +-
.../org/apache/camel/catalog/models/xquery.json | 7 +-
.../org/apache/camel/catalog/models/xtokenize.json | 7 +-
.../apache/camel/catalog/schemas/camel-spring.xsd | 13 ++-
.../apache/camel/catalog/schemas/camel-xml-io.xsd | 13 ++-
.../camel/catalog/schemas/camelYamlDsl-model.json | 15 ++++
.../org/apache/camel/language/xquery/xquery.json | 7 +-
.../camel-saxon/src/main/docs/xquery-language.adoc | 64 ++++++++++++++
.../xpath/XPathNamespacesRefSaxonTest.java | 89 +++++++++++++++++++
.../language/xquery/XQueryNamespacesRefTest.java | 69 +++++++++++++++
.../camel/language/xtokenizer/xtokenize.json | 7 +-
.../src/main/docs/xtokenize-language.adoc | 61 +++++++++++++
.../xtokenizer/XMLTokenizeNamespacesRefTest.java | 73 ++++++++++++++++
.../org/apache/camel/language/xpath/xpath.json | 7 +-
.../camel-xpath/src/main/docs/xpath-language.adoc | 80 +++++++++++++++++
.../org/apache/camel/model/language/xpath.json | 7 +-
.../org/apache/camel/model/language/xquery.json | 7 +-
.../org/apache/camel/model/language/xtokenize.json | 7 +-
.../model/language/NamespaceAwareExpression.java | 27 ++++++
.../language/XMLTokenizerExpressionReifier.java | 28 +++---
.../reifier/language/XPathExpressionReifier.java | 28 +++---
.../reifier/language/XQueryExpressionReifier.java | 28 +++---
.../camel/builder/xml/XPathNamespacesRefTest.java | 61 +++++++++++++
.../apache/camel/java/out/JavaDslModelWriter.java | 12 ++-
.../java/org/apache/camel/xml/in/ModelParser.java | 12 ++-
.../java/org/apache/camel/xml/out/ModelWriter.java | 12 ++-
.../org/apache/camel/yaml/out/YamlModelWriter.java | 12 ++-
.../dsl/xml/io/XmlXPathNamespacesRefTest.java | 95 +++++++++++++++++++++
.../dsl/yaml/deserializers/ModelDeserializers.java | 18 ++++
.../resources/schema/camelYamlDsl-canonical.json | 15 ++++
.../resources/schema/camelYamlDsl-model.json | 15 ++++
.../generated/resources/schema/camelYamlDsl.json | 15 ++++
.../dsl/yaml/XPathNamespacesRefYamlDslTest.java | 99 ++++++++++++++++++++++
39 files changed, 1139 insertions(+), 104 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xpath-language.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xpath-language.adoc
index b3d35829e946..cb9d63a6009d 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xpath-language.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xpath-language.adoc
@@ -27,6 +27,86 @@ include::partial$language-options.adoc[]
You can use namespaces with XPath expressions using the `Namespaces` helper
class.
+=== Sharing namespaces via a registry bean
+
+When the same prefix to URI mappings are needed by several expressions, you
can register a
+single `org.apache.camel.support.builder.Namespaces` bean and refer to it from
each
+expression with the `namespacesRef` option, instead of repeating the mappings
inline.
+
+This is intended for XML and YAML DSL, where a `Namespaces` instance cannot be
passed
+programmatically. In Java DSL you keep passing the builder directly.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+Namespaces ns = new Namespaces("c", "http://acme.com/cheese")
+ .add("w", "http://acme.com/wine");
+
+from("direct:start")
+ .filter(xpath("/c:number = 55", ns))
+ .to("mock:result");
+----
+
+XML::
++
+[source,xml]
+----
+<camel xmlns="http://camel.apache.org/schema/xml-io">
+
+ <bean name="myNamespaces" type="org.apache.camel.support.builder.Namespaces">
+ <properties>
+ <property key="namespaces[c]" value="http://acme.com/cheese"/>
+ <property key="namespaces[w]" value="http://acme.com/wine"/>
+ </properties>
+ </bean>
+
+ <route>
+ <from uri="direct:start"/>
+ <filter>
+ <xpath namespacesRef="myNamespaces">/c:number = 55</xpath>
+ <to uri="mock:result"/>
+ </filter>
+ </route>
+
+</camel>
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myNamespaces
+ type: org.apache.camel.support.builder.Namespaces
+ properties:
+ namespaces[c]: "http://acme.com/cheese"
+ namespaces[w]: "http://acme.com/wine"
+- route:
+ from:
+ uri: "direct:start"
+ steps:
+ - filter:
+ expression:
+ xpath:
+ expression: "/c:number = 55"
+ namespacesRef: "myNamespaces"
+ steps:
+ - to: "mock:result"
+----
+====
+
+Each prefix is configured as a `namespaces[prefix]` property on the bean, with
the namespace
+URI as the value.
+
+If an expression declares namespaces inline as well, then the inline
namespaces win and
+`namespacesRef` is ignored.
+
+The `namespacesRef` option is also available on the
xref:xquery-language.adoc[XQuery] and
+xref:xtokenize-language.adoc[XML Tokenize] languages.
+
== Variables
Variables in XPath are defined in different namespaces.
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xquery-language.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xquery-language.adoc
index 5bb3c9829db0..03246085d3b7 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xquery-language.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xquery-language.adoc
@@ -198,6 +198,70 @@ in the line with `xmlns:foo="http://example.com/person"`:
This namespace uses `foo` as prefix, so the `<xquery>` expression uses `foo:`
to use this namespace.
+=== Sharing namespaces via a registry bean
+
+Declaring the prefixes on the XML root tag only works when the route itself is
written in XML.
+To share one set of prefix to URI mappings across several expressions,
register a
+`org.apache.camel.support.builder.Namespaces` bean and refer to it with the
`namespacesRef`
+option:
+
+[tabs]
+====
+XML::
++
+[source,xml]
+----
+<camel xmlns="http://camel.apache.org/schema/xml-io">
+
+ <bean name="myNamespaces" type="org.apache.camel.support.builder.Namespaces">
+ <properties>
+ <property key="namespaces[c]" value="http://acme.com/cheese"/>
+ <property key="namespaces[w]" value="http://acme.com/wine"/>
+ </properties>
+ </bean>
+
+ <route>
+ <from uri="direct:start"/>
+ <filter>
+ <xquery namespacesRef="myNamespaces">/c:person[@name='James']</xquery>
+ <to uri="mock:result"/>
+ </filter>
+ </route>
+
+</camel>
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myNamespaces
+ type: org.apache.camel.support.builder.Namespaces
+ properties:
+ namespaces[c]: "http://acme.com/cheese"
+ namespaces[w]: "http://acme.com/wine"
+- route:
+ from:
+ uri: "direct:start"
+ steps:
+ - filter:
+ expression:
+ xquery:
+ expression: "/c:person[@name='James']"
+ namespacesRef: "myNamespaces"
+ steps:
+ - to: "mock:result"
+----
+====
+
+Each prefix is configured as a `namespaces[prefix]` property on the bean, with
the namespace
+URI as the value. In Java DSL there is no need for this, as the `Namespaces`
builder is passed
+to the expression directly, as shown above.
+
+If an expression declares namespaces inline as well, then the inline
namespaces win and
+`namespacesRef` is ignored.
+
== Using XQuery as transformation
We can do a message translation using transform or setBody in the route,
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xtokenize-language.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xtokenize-language.adoc
index 838dd9b9bddb..ef1cc8255696 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xtokenize-language.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xtokenize-language.adoc
@@ -23,6 +23,67 @@ but also more efficiently tokenizing XML documents than the
conventional xref:to
include::partial$language-options.adoc[]
// language options: END
+== Namespaces
+
+The XML Tokenize language is namespace aware, so a tokenized element can be
selected by its
+prefix. The prefix to URI mappings can be declared inline, or shared across
several expressions
+by registering a `org.apache.camel.support.builder.Namespaces` bean and
referring to it with
+the `namespacesRef` option:
+
+[tabs]
+====
+XML::
++
+[source,xml]
+----
+<camel xmlns="http://camel.apache.org/schema/xml-io">
+
+ <bean name="myNamespaces" type="org.apache.camel.support.builder.Namespaces">
+ <properties>
+ <property key="namespaces[c]" value="http://acme.com/cheese"/>
+ </properties>
+ </bean>
+
+ <route>
+ <from uri="file:inbox"/>
+ <split>
+ <xtokenize namespacesRef="myNamespaces">//c:order</xtokenize>
+ <to uri="mock:result"/>
+ </split>
+ </route>
+
+</camel>
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myNamespaces
+ type: org.apache.camel.support.builder.Namespaces
+ properties:
+ namespaces[c]: "http://acme.com/cheese"
+- route:
+ from:
+ uri: "file:inbox"
+ steps:
+ - split:
+ expression:
+ xtokenize:
+ expression: "//c:order"
+ namespacesRef: "myNamespaces"
+ steps:
+ - to: "mock:result"
+----
+====
+
+Each prefix is configured as a `namespaces[prefix]` property on the bean, with
the namespace
+URI as the value.
+
+If the expression declares namespaces inline as well, then the inline
namespaces win and
+`namespacesRef` is ignored.
+
== Example
See xref:eips:split-eip.adoc[Split EIP], which has examples using the XML
Tokenize language.
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xpath.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xpath.json
index df054242c695..e7d63b3180cd 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xpath.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xpath.json
@@ -27,8 +27,9 @@
"threadSafety": { "index": 8, "kind": "attribute", "displayName": "Thread
Safety", "group": "advanced", "label": "advanced", "required": false, "type":
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Whether to
enable thread-safety for the returned result of the xpath expression. This
applies to when using NODESET as the result type, and the returned set has
multiple elements." },
"preCompile": { "index": 9, "kind": "attribute", "displayName": "Pre
Compile", "group": "advanced", "label": "advanced", "required": false, "type":
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": true, "description": "Whether to enable
pre-compiling the xpath expression during initialization phase. pre-compile is
enabled by default." },
"namespace": { "index": 10, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 11, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 12, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 13, "kind": "attribute", "displayName": "Trim",
"group": "advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 11, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 12, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 13, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 14, "kind": "attribute", "displayName": "Trim",
"group": "advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xquery.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xquery.json
index 56d3cd94a531..7f72b0d644f0 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xquery.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xquery.json
@@ -20,8 +20,9 @@
"expression": { "index": 1, "kind": "value", "displayName": "Expression",
"group": "common", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The expression value in your chosen language syntax." },
"configurationRef": { "index": 2, "kind": "attribute", "displayName":
"Configuration Ref", "group": "advanced", "label": "advanced", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a saxon
configuration instance in the registry to use for xquery (requires
camel-saxon)." },
"namespace": { "index": 3, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 4, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 5, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 6, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 4, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 5, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 6, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 7, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xtokenize.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xtokenize.json
index 4c7b65c9c1ff..2ee646173188 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xtokenize.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xtokenize.json
@@ -21,8 +21,9 @@
"mode": { "index": 2, "kind": "attribute", "displayName": "Mode", "group":
"common", "required": false, "type": "enum", "javaType": "java.lang.String",
"enum": [ "i", "w", "u", "t" ], "deprecated": false, "autowired": false,
"secret": false, "defaultValue": "i", "description": "The extraction mode. The
available extraction modes are: i - injecting the contextual namespace bindings
into the extracted token (default), w - wrapping the extracted token in its
ancestor context, u - unwrap [...]
"group": { "index": 3, "kind": "attribute", "displayName": "Group",
"group": "common", "required": false, "type": "integer", "javaType":
"java.lang.Integer", "deprecated": false, "autowired": false, "secret": false,
"description": "To group N parts together." },
"namespace": { "index": 4, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 5, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 6, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 7, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 5, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 6, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 7, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 8, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xpath.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xpath.json
index 383ba5c1ebfe..b0dc94d5a4df 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xpath.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xpath.json
@@ -24,8 +24,9 @@
"threadSafety": { "index": 8, "kind": "attribute", "displayName": "Thread
Safety", "group": "advanced", "label": "advanced", "required": false, "type":
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Whether to
enable thread-safety for the returned result of the xpath expression. This
applies to when using NODESET as the result type, and the returned set has
multiple elements." },
"preCompile": { "index": 9, "kind": "attribute", "displayName": "Pre
Compile", "group": "advanced", "label": "advanced", "required": false, "type":
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": true, "description": "Whether to enable
pre-compiling the xpath expression during initialization phase. pre-compile is
enabled by default." },
"namespace": { "index": 10, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 11, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 12, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 13, "kind": "attribute", "displayName": "Trim",
"group": "advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 11, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 12, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 13, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 14, "kind": "attribute", "displayName": "Trim",
"group": "advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xquery.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xquery.json
index 445f35d90906..c85a4e3aef61 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xquery.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xquery.json
@@ -17,8 +17,9 @@
"expression": { "index": 1, "kind": "value", "displayName": "Expression",
"group": "common", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The expression value in your chosen language syntax." },
"configurationRef": { "index": 2, "kind": "attribute", "displayName":
"Configuration Ref", "group": "advanced", "label": "advanced", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a saxon
configuration instance in the registry to use for xquery (requires
camel-saxon)." },
"namespace": { "index": 3, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 4, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 5, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 6, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 4, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 5, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 6, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 7, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xtokenize.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xtokenize.json
index eae35f1315e3..91daa9ca76f1 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xtokenize.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/xtokenize.json
@@ -18,8 +18,9 @@
"mode": { "index": 2, "kind": "attribute", "displayName": "Mode", "group":
"common", "required": false, "type": "enum", "javaType": "java.lang.String",
"enum": [ "i", "w", "u", "t" ], "deprecated": false, "autowired": false,
"secret": false, "defaultValue": "i", "description": "The extraction mode. The
available extraction modes are: i - injecting the contextual namespace bindings
into the extracted token (default), w - wrapping the extracted token in its
ancestor context, u - unwrap [...]
"group": { "index": 3, "kind": "attribute", "displayName": "Group",
"group": "common", "required": false, "type": "integer", "javaType":
"java.lang.Integer", "deprecated": false, "autowired": false, "secret": false,
"description": "To group N parts together." },
"namespace": { "index": 4, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 5, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 6, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 7, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 5, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 6, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 7, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 8, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index be72acc1fe6c..70097ff0aea6 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -16318,7 +16318,18 @@ Whether to validate the bean has the configured
method. Default value: true
</xs:complexType>
<xs:complexType abstract="true" name="namespaceAwareExpression">
<xs:simpleContent>
- <xs:extension base="tns:singleInputTypedExpressionDefinition"/>
+ <xs:extension base="tns:singleInputTypedExpressionDefinition">
+ <xs:attribute name="namespacesRef" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+<![CDATA[
+Reference to a org.apache.camel.support.builder.Namespaces bean in the
registry to use for the XML Namespaces of prefix
+to uri mappings.
+]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="ognlExpression">
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
index 3fff6f740797..a628dc317c20 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
@@ -15461,7 +15461,18 @@ Whether to validate the bean has the configured
method. Default value: true
</xs:complexType>
<xs:complexType abstract="true" name="namespaceAwareExpression">
<xs:simpleContent>
- <xs:extension base="tns:singleInputTypedExpressionDefinition"/>
+ <xs:extension base="tns:singleInputTypedExpressionDefinition">
+ <xs:attribute name="namespacesRef" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+<![CDATA[
+Reference to a org.apache.camel.support.builder.Namespaces bean in the
registry to use for the XML Namespaces of prefix
+to uri mappings.
+]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="ognlExpression">
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
index 2a19bbe7cccc..2a965559ce02 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
@@ -15978,6 +15978,11 @@
"ref" : "property",
"description" : "Injects the XML Namespaces of prefix to uri
mappings.",
"title" : "Namespace"
+ }, {
+ "name" : "namespacesRef",
+ "type" : "string",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings.",
+ "title" : "Namespaces Ref"
}, {
"name" : "resultType",
"type" : "string",
@@ -16032,6 +16037,11 @@
"ref" : "property",
"description" : "Injects the XML Namespaces of prefix to uri
mappings.",
"title" : "Namespace"
+ }, {
+ "name" : "namespacesRef",
+ "type" : "string",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings.",
+ "title" : "Namespaces Ref"
}, {
"name" : "objectModel",
"type" : "string",
@@ -16105,6 +16115,11 @@
"ref" : "property",
"description" : "Injects the XML Namespaces of prefix to uri
mappings.",
"title" : "Namespace"
+ }, {
+ "name" : "namespacesRef",
+ "type" : "string",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings.",
+ "title" : "Namespaces Ref"
}, {
"name" : "resultType",
"type" : "string",
diff --git
a/components/camel-saxon/src/generated/resources/META-INF/org/apache/camel/language/xquery/xquery.json
b/components/camel-saxon/src/generated/resources/META-INF/org/apache/camel/language/xquery/xquery.json
index 56d3cd94a531..7f72b0d644f0 100644
---
a/components/camel-saxon/src/generated/resources/META-INF/org/apache/camel/language/xquery/xquery.json
+++
b/components/camel-saxon/src/generated/resources/META-INF/org/apache/camel/language/xquery/xquery.json
@@ -20,8 +20,9 @@
"expression": { "index": 1, "kind": "value", "displayName": "Expression",
"group": "common", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The expression value in your chosen language syntax." },
"configurationRef": { "index": 2, "kind": "attribute", "displayName":
"Configuration Ref", "group": "advanced", "label": "advanced", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a saxon
configuration instance in the registry to use for xquery (requires
camel-saxon)." },
"namespace": { "index": 3, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 4, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 5, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 6, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 4, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 5, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 6, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 7, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git a/components/camel-saxon/src/main/docs/xquery-language.adoc
b/components/camel-saxon/src/main/docs/xquery-language.adoc
index 5bb3c9829db0..03246085d3b7 100644
--- a/components/camel-saxon/src/main/docs/xquery-language.adoc
+++ b/components/camel-saxon/src/main/docs/xquery-language.adoc
@@ -198,6 +198,70 @@ in the line with `xmlns:foo="http://example.com/person"`:
This namespace uses `foo` as prefix, so the `<xquery>` expression uses `foo:`
to use this namespace.
+=== Sharing namespaces via a registry bean
+
+Declaring the prefixes on the XML root tag only works when the route itself is
written in XML.
+To share one set of prefix to URI mappings across several expressions,
register a
+`org.apache.camel.support.builder.Namespaces` bean and refer to it with the
`namespacesRef`
+option:
+
+[tabs]
+====
+XML::
++
+[source,xml]
+----
+<camel xmlns="http://camel.apache.org/schema/xml-io">
+
+ <bean name="myNamespaces" type="org.apache.camel.support.builder.Namespaces">
+ <properties>
+ <property key="namespaces[c]" value="http://acme.com/cheese"/>
+ <property key="namespaces[w]" value="http://acme.com/wine"/>
+ </properties>
+ </bean>
+
+ <route>
+ <from uri="direct:start"/>
+ <filter>
+ <xquery namespacesRef="myNamespaces">/c:person[@name='James']</xquery>
+ <to uri="mock:result"/>
+ </filter>
+ </route>
+
+</camel>
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myNamespaces
+ type: org.apache.camel.support.builder.Namespaces
+ properties:
+ namespaces[c]: "http://acme.com/cheese"
+ namespaces[w]: "http://acme.com/wine"
+- route:
+ from:
+ uri: "direct:start"
+ steps:
+ - filter:
+ expression:
+ xquery:
+ expression: "/c:person[@name='James']"
+ namespacesRef: "myNamespaces"
+ steps:
+ - to: "mock:result"
+----
+====
+
+Each prefix is configured as a `namespaces[prefix]` property on the bean, with
the namespace
+URI as the value. In Java DSL there is no need for this, as the `Namespaces`
builder is passed
+to the expression directly, as shown above.
+
+If an expression declares namespaces inline as well, then the inline
namespaces win and
+`namespacesRef` is ignored.
+
== Using XQuery as transformation
We can do a message translation using transform or setBody in the route,
diff --git
a/components/camel-saxon/src/test/java/org/apache/camel/language/xpath/XPathNamespacesRefSaxonTest.java
b/components/camel-saxon/src/test/java/org/apache/camel/language/xpath/XPathNamespacesRefSaxonTest.java
new file mode 100644
index 000000000000..273e0ae512aa
--- /dev/null
+++
b/components/camel-saxon/src/test/java/org/apache/camel/language/xpath/XPathNamespacesRefSaxonTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.xpath;
+
+import net.sf.saxon.xpath.XPathFactoryImpl;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.Predicate;
+import org.apache.camel.model.language.XPathExpression;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.support.builder.Namespaces;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests namespacesRef with Saxon, which resolves namespace prefixes when the
expression is compiled rather than when it
+ * is evaluated. The referenced {@link Namespaces} bean must therefore be
resolved before the expression is built, not
+ * afterwards, or compilation fails with "Namespace prefix has not been
declared".
+ */
+class XPathNamespacesRefSaxonTest extends CamelTestSupport {
+
+ @Override
+ protected void bindToRegistry(Registry registry) {
+ registry.bind("myNamespaces", new Namespaces("c",
"http://acme.com/cheese").add("w", "http://acme.com/wine"));
+ registry.bind("saxonFactory", new XPathFactoryImpl());
+ }
+
+ @Test
+ void testXPathWithNamespacesRef() {
+ Predicate cheese = predicate("/c:number = 55");
+ Predicate wine = predicate("/w:number = 77");
+
+ // 1 positive example per namespace
+ assertTrue(cheese.matches(exchange("<number
xmlns=\"http://acme.com/cheese\">55</number>")));
+ assertTrue(wine.matches(exchange("<number
xmlns=\"http://acme.com/wine\">77</number>")));
+
+ // negative, data-wise: correct namespace, wrong value
+ assertFalse(cheese.matches(exchange("<number
xmlns=\"http://acme.com/cheese\">99</number>")));
+
+ // negative, namespace-wise: correct value, wrong namespace URI
+ assertFalse(wine.matches(exchange("<number
xmlns=\"http://acme.com/water\">77</number>")));
+ }
+
+ @Test
+ void testXPathWithNamespacesRefAndResultType() {
+ // a resultType makes the language return a converting wrapper rather
than the NamespaceAware builder,
+ // so the referenced namespaces must already be applied when the
expression is built
+ XPathExpression definition = new XPathExpression("/c:number");
+ definition.setNamespacesRef("myNamespaces");
+ definition.setResultType(String.class);
+ definition.setFactoryRef("saxonFactory");
+ Expression expression = definition.createExpression(context);
+
+ assertEquals("55", expression.evaluate(exchange("<number
xmlns=\"http://acme.com/cheese\">55</number>"), String.class));
+ }
+
+ private Predicate predicate(String expression) {
+ XPathExpression definition = new XPathExpression(expression);
+ definition.setNamespacesRef("myNamespaces");
+ definition.setResultQName("BOOLEAN");
+ definition.setFactoryRef("saxonFactory");
+ return definition.createPredicate(context);
+ }
+
+ private Exchange exchange(String body) {
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setBody(body);
+ return exchange;
+ }
+}
diff --git
a/components/camel-saxon/src/test/java/org/apache/camel/language/xquery/XQueryNamespacesRefTest.java
b/components/camel-saxon/src/test/java/org/apache/camel/language/xquery/XQueryNamespacesRefTest.java
new file mode 100644
index 000000000000..d8313fad8e3e
--- /dev/null
+++
b/components/camel-saxon/src/test/java/org/apache/camel/language/xquery/XQueryNamespacesRefTest.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.xquery;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Predicate;
+import org.apache.camel.model.language.XQueryExpression;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.support.builder.Namespaces;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests that an xquery expression can reference a {@link Namespaces} bean
from the registry via namespacesRef, instead
+ * of declaring the namespaces inline.
+ */
+class XQueryNamespacesRefTest extends CamelTestSupport {
+
+ @Override
+ protected void bindToRegistry(Registry registry) {
+ registry.bind("myNamespaces", new Namespaces("c",
"http://acme.com/cheese").add("w", "http://acme.com/wine"));
+ }
+
+ @Test
+ void testXQueryWithNamespacesRef() {
+ Predicate cheese = predicate("/c:person[@name='James']");
+ Predicate wine = predicate("/w:person[@name='James']");
+
+ // 1 positive example per namespace
+ assertTrue(cheese.matches(exchange("<person
xmlns=\"http://acme.com/cheese\" name=\"James\"/>")));
+ assertTrue(wine.matches(exchange("<person
xmlns=\"http://acme.com/wine\" name=\"James\"/>")));
+
+ // negative, data-wise: correct namespace, wrong value
+ assertFalse(cheese.matches(exchange("<person
xmlns=\"http://acme.com/cheese\" name=\"Claus\"/>")));
+
+ // negative, namespace-wise: correct value, wrong namespace URI
+ assertFalse(wine.matches(exchange("<person
xmlns=\"http://acme.com/water\" name=\"James\"/>")));
+ }
+
+ private Predicate predicate(String expression) {
+ XQueryExpression definition = new XQueryExpression(expression);
+ definition.setNamespacesRef("myNamespaces");
+ return definition.createPredicate(context);
+ }
+
+ private Exchange exchange(String body) {
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setBody(body);
+ return exchange;
+ }
+}
diff --git
a/components/camel-stax/src/generated/resources/META-INF/org/apache/camel/language/xtokenizer/xtokenize.json
b/components/camel-stax/src/generated/resources/META-INF/org/apache/camel/language/xtokenizer/xtokenize.json
index 4c7b65c9c1ff..2ee646173188 100644
---
a/components/camel-stax/src/generated/resources/META-INF/org/apache/camel/language/xtokenizer/xtokenize.json
+++
b/components/camel-stax/src/generated/resources/META-INF/org/apache/camel/language/xtokenizer/xtokenize.json
@@ -21,8 +21,9 @@
"mode": { "index": 2, "kind": "attribute", "displayName": "Mode", "group":
"common", "required": false, "type": "enum", "javaType": "java.lang.String",
"enum": [ "i", "w", "u", "t" ], "deprecated": false, "autowired": false,
"secret": false, "defaultValue": "i", "description": "The extraction mode. The
available extraction modes are: i - injecting the contextual namespace bindings
into the extracted token (default), w - wrapping the extracted token in its
ancestor context, u - unwrap [...]
"group": { "index": 3, "kind": "attribute", "displayName": "Group",
"group": "common", "required": false, "type": "integer", "javaType":
"java.lang.Integer", "deprecated": false, "autowired": false, "secret": false,
"description": "To group N parts together." },
"namespace": { "index": 4, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 5, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 6, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 7, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 5, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 6, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 7, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 8, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git a/components/camel-stax/src/main/docs/xtokenize-language.adoc
b/components/camel-stax/src/main/docs/xtokenize-language.adoc
index 838dd9b9bddb..ef1cc8255696 100644
--- a/components/camel-stax/src/main/docs/xtokenize-language.adoc
+++ b/components/camel-stax/src/main/docs/xtokenize-language.adoc
@@ -23,6 +23,67 @@ but also more efficiently tokenizing XML documents than the
conventional xref:to
include::partial$language-options.adoc[]
// language options: END
+== Namespaces
+
+The XML Tokenize language is namespace aware, so a tokenized element can be
selected by its
+prefix. The prefix to URI mappings can be declared inline, or shared across
several expressions
+by registering a `org.apache.camel.support.builder.Namespaces` bean and
referring to it with
+the `namespacesRef` option:
+
+[tabs]
+====
+XML::
++
+[source,xml]
+----
+<camel xmlns="http://camel.apache.org/schema/xml-io">
+
+ <bean name="myNamespaces" type="org.apache.camel.support.builder.Namespaces">
+ <properties>
+ <property key="namespaces[c]" value="http://acme.com/cheese"/>
+ </properties>
+ </bean>
+
+ <route>
+ <from uri="file:inbox"/>
+ <split>
+ <xtokenize namespacesRef="myNamespaces">//c:order</xtokenize>
+ <to uri="mock:result"/>
+ </split>
+ </route>
+
+</camel>
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myNamespaces
+ type: org.apache.camel.support.builder.Namespaces
+ properties:
+ namespaces[c]: "http://acme.com/cheese"
+- route:
+ from:
+ uri: "file:inbox"
+ steps:
+ - split:
+ expression:
+ xtokenize:
+ expression: "//c:order"
+ namespacesRef: "myNamespaces"
+ steps:
+ - to: "mock:result"
+----
+====
+
+Each prefix is configured as a `namespaces[prefix]` property on the bean, with
the namespace
+URI as the value.
+
+If the expression declares namespaces inline as well, then the inline
namespaces win and
+`namespacesRef` is ignored.
+
== Example
See xref:eips:split-eip.adoc[Split EIP], which has examples using the XML
Tokenize language.
diff --git
a/components/camel-stax/src/test/java/org/apache/camel/language/xtokenizer/XMLTokenizeNamespacesRefTest.java
b/components/camel-stax/src/test/java/org/apache/camel/language/xtokenizer/XMLTokenizeNamespacesRefTest.java
new file mode 100644
index 000000000000..f05cf67900c8
--- /dev/null
+++
b/components/camel-stax/src/test/java/org/apache/camel/language/xtokenizer/XMLTokenizeNamespacesRefTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.xtokenizer;
+
+import java.util.Iterator;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.model.language.XMLTokenizerExpression;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.support.builder.Namespaces;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests that an xtokenize expression can reference a {@link Namespaces} bean
from the registry via namespacesRef,
+ * instead of declaring the namespaces inline.
+ */
+class XMLTokenizeNamespacesRefTest extends CamelTestSupport {
+
+ @Override
+ protected void bindToRegistry(Registry registry) {
+ registry.bind("myNamespaces", new Namespaces("c",
"http://acme.com/cheese").add("w", "http://acme.com/wine"));
+ }
+
+ @Test
+ void testXTokenizeWithNamespacesRef() {
+ Iterator<?> cheese
+ = tokenize("//c:order", "<orders
xmlns:c=\"http://acme.com/cheese\"><c:order>one</c:order></orders>");
+ Iterator<?> wine = tokenize("//w:order", "<orders
xmlns:w=\"http://acme.com/wine\"><w:order>two</w:order></orders>");
+
+ // 1 positive example per namespace
+ assertTrue(cheese.hasNext());
+ assertTrue(String.valueOf(cheese.next()).contains("one"));
+ assertTrue(wine.hasNext());
+ assertTrue(String.valueOf(wine.next()).contains("two"));
+
+ // negative, namespace-wise: matching local name, wrong namespace URI
+ Iterator<?> water
+ = tokenize("//c:order", "<orders
xmlns:c=\"http://acme.com/water\"><c:order>three</c:order></orders>");
+ assertFalse(water.hasNext());
+ }
+
+ private Iterator<?> tokenize(String expression, String body) {
+ XMLTokenizerExpression definition = new
XMLTokenizerExpression(expression);
+ definition.setNamespacesRef("myNamespaces");
+ Expression exp = definition.createExpression(context);
+
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setBody(body);
+
+ return assertInstanceOf(Iterator.class, exp.evaluate(exchange,
Object.class));
+ }
+}
diff --git
a/components/camel-xpath/src/generated/resources/META-INF/org/apache/camel/language/xpath/xpath.json
b/components/camel-xpath/src/generated/resources/META-INF/org/apache/camel/language/xpath/xpath.json
index df054242c695..e7d63b3180cd 100644
---
a/components/camel-xpath/src/generated/resources/META-INF/org/apache/camel/language/xpath/xpath.json
+++
b/components/camel-xpath/src/generated/resources/META-INF/org/apache/camel/language/xpath/xpath.json
@@ -27,8 +27,9 @@
"threadSafety": { "index": 8, "kind": "attribute", "displayName": "Thread
Safety", "group": "advanced", "label": "advanced", "required": false, "type":
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Whether to
enable thread-safety for the returned result of the xpath expression. This
applies to when using NODESET as the result type, and the returned set has
multiple elements." },
"preCompile": { "index": 9, "kind": "attribute", "displayName": "Pre
Compile", "group": "advanced", "label": "advanced", "required": false, "type":
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": true, "description": "Whether to enable
pre-compiling the xpath expression during initialization phase. pre-compile is
enabled by default." },
"namespace": { "index": 10, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 11, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 12, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 13, "kind": "attribute", "displayName": "Trim",
"group": "advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 11, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 12, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 13, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 14, "kind": "attribute", "displayName": "Trim",
"group": "advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git a/components/camel-xpath/src/main/docs/xpath-language.adoc
b/components/camel-xpath/src/main/docs/xpath-language.adoc
index b3d35829e946..cb9d63a6009d 100644
--- a/components/camel-xpath/src/main/docs/xpath-language.adoc
+++ b/components/camel-xpath/src/main/docs/xpath-language.adoc
@@ -27,6 +27,86 @@ include::partial$language-options.adoc[]
You can use namespaces with XPath expressions using the `Namespaces` helper
class.
+=== Sharing namespaces via a registry bean
+
+When the same prefix to URI mappings are needed by several expressions, you
can register a
+single `org.apache.camel.support.builder.Namespaces` bean and refer to it from
each
+expression with the `namespacesRef` option, instead of repeating the mappings
inline.
+
+This is intended for XML and YAML DSL, where a `Namespaces` instance cannot be
passed
+programmatically. In Java DSL you keep passing the builder directly.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+Namespaces ns = new Namespaces("c", "http://acme.com/cheese")
+ .add("w", "http://acme.com/wine");
+
+from("direct:start")
+ .filter(xpath("/c:number = 55", ns))
+ .to("mock:result");
+----
+
+XML::
++
+[source,xml]
+----
+<camel xmlns="http://camel.apache.org/schema/xml-io">
+
+ <bean name="myNamespaces" type="org.apache.camel.support.builder.Namespaces">
+ <properties>
+ <property key="namespaces[c]" value="http://acme.com/cheese"/>
+ <property key="namespaces[w]" value="http://acme.com/wine"/>
+ </properties>
+ </bean>
+
+ <route>
+ <from uri="direct:start"/>
+ <filter>
+ <xpath namespacesRef="myNamespaces">/c:number = 55</xpath>
+ <to uri="mock:result"/>
+ </filter>
+ </route>
+
+</camel>
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myNamespaces
+ type: org.apache.camel.support.builder.Namespaces
+ properties:
+ namespaces[c]: "http://acme.com/cheese"
+ namespaces[w]: "http://acme.com/wine"
+- route:
+ from:
+ uri: "direct:start"
+ steps:
+ - filter:
+ expression:
+ xpath:
+ expression: "/c:number = 55"
+ namespacesRef: "myNamespaces"
+ steps:
+ - to: "mock:result"
+----
+====
+
+Each prefix is configured as a `namespaces[prefix]` property on the bean, with
the namespace
+URI as the value.
+
+If an expression declares namespaces inline as well, then the inline
namespaces win and
+`namespacesRef` is ignored.
+
+The `namespacesRef` option is also available on the
xref:xquery-language.adoc[XQuery] and
+xref:xtokenize-language.adoc[XML Tokenize] languages.
+
== Variables
Variables in XPath are defined in different namespaces.
diff --git
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xpath.json
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xpath.json
index 383ba5c1ebfe..b0dc94d5a4df 100644
---
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xpath.json
+++
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xpath.json
@@ -24,8 +24,9 @@
"threadSafety": { "index": 8, "kind": "attribute", "displayName": "Thread
Safety", "group": "advanced", "label": "advanced", "required": false, "type":
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Whether to
enable thread-safety for the returned result of the xpath expression. This
applies to when using NODESET as the result type, and the returned set has
multiple elements." },
"preCompile": { "index": 9, "kind": "attribute", "displayName": "Pre
Compile", "group": "advanced", "label": "advanced", "required": false, "type":
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": true, "description": "Whether to enable
pre-compiling the xpath expression during initialization phase. pre-compile is
enabled by default." },
"namespace": { "index": 10, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 11, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 12, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 13, "kind": "attribute", "displayName": "Trim",
"group": "advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 11, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 12, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 13, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 14, "kind": "attribute", "displayName": "Trim",
"group": "advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xquery.json
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xquery.json
index 445f35d90906..c85a4e3aef61 100644
---
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xquery.json
+++
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xquery.json
@@ -17,8 +17,9 @@
"expression": { "index": 1, "kind": "value", "displayName": "Expression",
"group": "common", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The expression value in your chosen language syntax." },
"configurationRef": { "index": 2, "kind": "attribute", "displayName":
"Configuration Ref", "group": "advanced", "label": "advanced", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a saxon
configuration instance in the registry to use for xquery (requires
camel-saxon)." },
"namespace": { "index": 3, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 4, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 5, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 6, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 4, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 5, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 6, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 7, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
diff --git
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xtokenize.json
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xtokenize.json
index eae35f1315e3..91daa9ca76f1 100644
---
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xtokenize.json
+++
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/language/xtokenize.json
@@ -18,8 +18,9 @@
"mode": { "index": 2, "kind": "attribute", "displayName": "Mode", "group":
"common", "required": false, "type": "enum", "javaType": "java.lang.String",
"enum": [ "i", "w", "u", "t" ], "deprecated": false, "autowired": false,
"secret": false, "defaultValue": "i", "description": "The extraction mode. The
available extraction modes are: i - injecting the contextual namespace bindings
into the extracted token (default), w - wrapping the extracted token in its
ancestor context, u - unwrap [...]
"group": { "index": 3, "kind": "attribute", "displayName": "Group",
"group": "common", "required": false, "type": "integer", "javaType":
"java.lang.Integer", "deprecated": false, "autowired": false, "secret": false,
"description": "To group N parts together." },
"namespace": { "index": 4, "kind": "element", "displayName": "Namespace",
"group": "common", "label": "common", "required": false, "type": "array",
"javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>",
"deprecated": false, "autowired": false, "secret": false, "description":
"Injects the XML Namespaces of prefix to uri mappings." },
- "source": { "index": 5, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
- "resultType": { "index": 6, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
- "trim": { "index": 7, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
+ "namespacesRef": { "index": 5, "kind": "attribute", "displayName":
"Namespaces Ref", "group": "advanced", "label": "advanced", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings." },
+ "source": { "index": 6, "kind": "attribute", "displayName": "Source",
"group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body." },
+ "resultType": { "index": 7, "kind": "attribute", "displayName": "Result
Type", "group": "common", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "The class of the result type (type from output)." },
+ "trim": { "index": 8, "kind": "attribute", "displayName": "Trim", "group":
"advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Whether to trim the
source code to remove leading and trailing whitespaces and line breaks." }
}
}
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 6264651e532a..fc85ec7a6994 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
@@ -24,6 +24,7 @@ import java.util.Map;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlTransient;
@@ -45,6 +46,10 @@ public abstract class NamespaceAwareExpression extends
SingleInputTypedExpressio
private List<PropertyDefinition> namespace;
@XmlTransient
private Map<String, String> namespaces;
+ @XmlAttribute
+ @Metadata(label = "advanced",
+ description = "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings.")
+ private String namespacesRef;
protected NamespaceAwareExpression() {
}
@@ -53,6 +58,7 @@ public abstract class NamespaceAwareExpression extends
SingleInputTypedExpressio
super(source);
this.namespace =
ProcessorDefinitionHelper.deepCopyDefinitions(source.namespace);
this.namespaces = source.namespaces != null ? new
LinkedHashMap<>(source.namespaces) : null;
+ this.namespacesRef = source.namespacesRef;
}
protected NamespaceAwareExpression(String expression) {
@@ -63,6 +69,7 @@ public abstract class NamespaceAwareExpression extends
SingleInputTypedExpressio
super(builder);
this.namespace = builder.namespace;
this.namespaces = builder.namespaces;
+ this.namespacesRef = builder.namespacesRef;
}
@Override
@@ -87,6 +94,14 @@ public abstract class NamespaceAwareExpression extends
SingleInputTypedExpressio
this.namespace = namespace;
}
+ public String getNamespacesRef() {
+ return namespacesRef;
+ }
+
+ public void setNamespacesRef(String namespacesRef) {
+ this.namespacesRef = namespacesRef;
+ }
+
public Map<String, String> getNamespaceAsMap() {
if (namespaces == null && namespace != null) {
namespaces = new HashMap<>();
@@ -113,6 +128,7 @@ public abstract class NamespaceAwareExpression extends
SingleInputTypedExpressio
private List<PropertyDefinition> namespace;
private Map<String, String> namespaces;
+ private String namespacesRef;
/**
* Injects the XML Namespaces of prefix -> uri mappings
@@ -141,5 +157,16 @@ public abstract class NamespaceAwareExpression extends
SingleInputTypedExpressio
this.namespace = namespace;
return (T) this;
}
+
+ /**
+ * Reference to a {@link Namespaces} bean in the registry to use for
the XML Namespaces of prefix -> uri
+ * mappings
+ *
+ * @param namespacesRef the id of the Namespaces bean in the registry
+ */
+ public T namespacesRef(String namespacesRef) {
+ this.namespacesRef = namespacesRef;
+ return (T) this;
+ }
}
}
diff --git
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XMLTokenizerExpressionReifier.java
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XMLTokenizerExpressionReifier.java
index 344b49058198..883536f4300a 100644
---
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XMLTokenizerExpressionReifier.java
+++
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XMLTokenizerExpressionReifier.java
@@ -16,12 +16,12 @@
*/
package org.apache.camel.reifier.language;
+import java.util.Map;
+
import org.apache.camel.CamelContext;
-import org.apache.camel.Expression;
-import org.apache.camel.Predicate;
import org.apache.camel.model.language.ExpressionDefinition;
import org.apache.camel.model.language.XMLTokenizerExpression;
-import org.apache.camel.spi.NamespaceAware;
+import org.apache.camel.support.builder.Namespaces;
public class XMLTokenizerExpressionReifier extends
SingleInputTypedExpressionReifier<XMLTokenizerExpression> {
@@ -29,20 +29,14 @@ public class XMLTokenizerExpressionReifier extends
SingleInputTypedExpressionRei
super(camelContext, definition);
}
- @Override
- protected void configurePredicate(Predicate predicate) {
- configureNamespaceAware(predicate);
- }
-
- @Override
- protected void configureExpression(Expression expression) {
- configureNamespaceAware(expression);
- }
-
- protected void configureNamespaceAware(Object builder) {
- if (definition.getNamespaces() != null && builder instanceof
NamespaceAware namespaceAware) {
- namespaceAware.setNamespaces(parseMap(definition.getNamespaces()));
+ private Map<String, String> resolveNamespaces() {
+ if (definition.getNamespaces() != null &&
!definition.getNamespaces().isEmpty()) {
+ return parseMap(definition.getNamespaces());
+ }
+ if (definition.getNamespacesRef() != null) {
+ return parseMap(mandatoryLookup(definition.getNamespacesRef(),
Namespaces.class).getNamespaces());
}
+ return null;
}
protected Object[] createProperties() {
@@ -51,7 +45,7 @@ public class XMLTokenizerExpressionReifier extends
SingleInputTypedExpressionRei
properties[1] = parseString(definition.getSource());
properties[2] = parseString(definition.getMode());
properties[3] = parseInt(definition.getGroup());
- properties[4] = definition.getNamespaces();
+ properties[4] = resolveNamespaces();
return properties;
}
diff --git
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XPathExpressionReifier.java
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XPathExpressionReifier.java
index 7136d3dd8bbe..904ea8d83222 100644
---
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XPathExpressionReifier.java
+++
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XPathExpressionReifier.java
@@ -16,17 +16,17 @@
*/
package org.apache.camel.reifier.language;
+import java.util.Map;
+
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.apache.camel.CamelContext;
-import org.apache.camel.Expression;
-import org.apache.camel.Predicate;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.model.language.ExpressionDefinition;
import org.apache.camel.model.language.XPathExpression;
import org.apache.camel.spi.Language;
-import org.apache.camel.spi.NamespaceAware;
+import org.apache.camel.support.builder.Namespaces;
public class XPathExpressionReifier extends
SingleInputTypedExpressionReifier<XPathExpression> {
@@ -34,20 +34,14 @@ public class XPathExpressionReifier extends
SingleInputTypedExpressionReifier<XP
super(camelContext, definition);
}
- @Override
- protected void configurePredicate(Predicate predicate) {
- configureNamespaceAware(predicate);
- }
-
- @Override
- protected void configureExpression(Expression expression) {
- configureNamespaceAware(expression);
- }
-
- protected void configureNamespaceAware(Object builder) {
- if (definition.getNamespaces() != null && builder instanceof
NamespaceAware namespaceAware) {
- namespaceAware.setNamespaces(parseMap(definition.getNamespaces()));
+ private Map<String, String> resolveNamespaces() {
+ if (definition.getNamespaces() != null &&
!definition.getNamespaces().isEmpty()) {
+ return parseMap(definition.getNamespaces());
}
+ if (definition.getNamespacesRef() != null) {
+ return parseMap(mandatoryLookup(definition.getNamespacesRef(),
Namespaces.class).getNamespaces());
+ }
+ return null;
}
protected Object[] createProperties() {
@@ -62,7 +56,7 @@ public class XPathExpressionReifier extends
SingleInputTypedExpressionReifier<XP
properties[7] = parseBoolean(definition.getThreadSafety());
properties[8] = parseBoolean(definition.getPreCompile());
properties[9] = parseBoolean(definition.getLogNamespaces());
- properties[10] = definition.getNamespaces();
+ properties[10] = resolveNamespaces();
return properties;
}
diff --git
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XQueryExpressionReifier.java
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XQueryExpressionReifier.java
index b17affbfc990..a29d84563013 100644
---
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XQueryExpressionReifier.java
+++
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/XQueryExpressionReifier.java
@@ -16,13 +16,13 @@
*/
package org.apache.camel.reifier.language;
+import java.util.Map;
+
import org.apache.camel.CamelContext;
-import org.apache.camel.Expression;
-import org.apache.camel.Predicate;
import org.apache.camel.model.language.ExpressionDefinition;
import org.apache.camel.model.language.XQueryExpression;
import org.apache.camel.spi.Language;
-import org.apache.camel.spi.NamespaceAware;
+import org.apache.camel.support.builder.Namespaces;
public class XQueryExpressionReifier extends
SingleInputTypedExpressionReifier<XQueryExpression> {
@@ -30,27 +30,21 @@ public class XQueryExpressionReifier extends
SingleInputTypedExpressionReifier<X
super(camelContext, definition);
}
- @Override
- protected void configurePredicate(Predicate predicate) {
- configureNamespaceAware(predicate);
- }
-
- @Override
- protected void configureExpression(Expression expression) {
- configureNamespaceAware(expression);
- }
-
- protected void configureNamespaceAware(Object builder) {
- if (definition.getNamespaces() != null && builder instanceof
NamespaceAware namespaceAware) {
- namespaceAware.setNamespaces(parseMap(definition.getNamespaces()));
+ private Map<String, String> resolveNamespaces() {
+ if (definition.getNamespaces() != null &&
!definition.getNamespaces().isEmpty()) {
+ return parseMap(definition.getNamespaces());
+ }
+ if (definition.getNamespacesRef() != null) {
+ return parseMap(mandatoryLookup(definition.getNamespacesRef(),
Namespaces.class).getNamespaces());
}
+ return null;
}
protected Object[] createProperties() {
Object[] properties = new Object[3];
properties[0] = asResultType();
properties[1] = parseString(definition.getSource());
- properties[2] = definition.getNamespaces();
+ properties[2] = resolveNamespaces();
return properties;
}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/builder/xml/XPathNamespacesRefTest.java
b/core/camel-core/src/test/java/org/apache/camel/builder/xml/XPathNamespacesRefTest.java
new file mode 100644
index 000000000000..71bfea370095
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/builder/xml/XPathNamespacesRefTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.builder.xml;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Predicate;
+import org.apache.camel.model.language.XPathExpression;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.builder.Namespaces;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests that an xpath expression can reference a {@link Namespaces} bean from
the registry via namespacesRef, instead
+ * of declaring the namespaces inline.
+ */
+class XPathNamespacesRefTest extends ContextTestSupport {
+
+ @Override
+ protected void bindToRegistry(Registry registry) {
+ registry.bind("myNamespaces", new Namespaces("c",
"http://acme.com/cheese").add("w", "http://acme.com/wine"));
+ }
+
+ @Test
+ void testXPathWithNamespacesRef() {
+ Predicate cheese = predicate("/c:number = 55");
+ Predicate wine = predicate("/w:number = 77");
+
+ // 1 positive example per namespace
+ assertThat(cheese.matches(createExchangeWithBody("<number
xmlns=\"http://acme.com/cheese\">55</number>"))).isTrue();
+ assertThat(wine.matches(createExchangeWithBody("<number
xmlns=\"http://acme.com/wine\">77</number>"))).isTrue();
+
+ // negative, data-wise: correct namespace, wrong value
+ assertThat(cheese.matches(createExchangeWithBody("<number
xmlns=\"http://acme.com/cheese\">99</number>"))).isFalse();
+
+ // negative, namespace-wise: correct value, wrong namespace URI
+ assertThat(wine.matches(createExchangeWithBody("<number
xmlns=\"http://acme.com/water\">77</number>"))).isFalse();
+ }
+
+ private Predicate predicate(String expression) {
+ XPathExpression definition = new XPathExpression(expression);
+ definition.setNamespacesRef("myNamespaces");
+ definition.setResultQName("BOOLEAN");
+ return definition.createPredicate(context);
+ }
+}
diff --git
a/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
b/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
index b1d7fdf89b82..0fb08beb668b 100644
---
a/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
+++
b/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
@@ -3545,11 +3545,15 @@ public class JavaDslModelWriter extends
JavaDslModelWriterSupport {
doWriteTypedExpressionDefinitionAttributes(sb, def);
doWriteValue(sb, def.getExpression());
}
+ protected void doWriteNamespaceAwareExpressionAttributes(StringBuilder sb,
NamespaceAwareExpression def) {
+ doWriteSingleInputTypedExpressionDefinitionAttributes(sb, def);
+ doWriteAttribute(sb, "namespacesRef", def.getNamespacesRef(), null);
+ }
protected void doWriteNamespaceAwareExpressionElements(StringBuilder sb,
NamespaceAwareExpression def) {
doWriteChildList(sb, "namespace", def.getNamespace(),
this::doWritePropertyDefinition);
}
protected void doWriteNamespaceAwareExpression(StringBuilder sb,
NamespaceAwareExpression def) {
- doWriteSingleInputTypedExpressionDefinitionAttributes(sb, def);
+ doWriteNamespaceAwareExpressionAttributes(sb, def);
doWriteValue(sb, def.getExpression());
doWriteNamespaceAwareExpressionElements(sb, def);
}
@@ -3623,14 +3627,14 @@ public class JavaDslModelWriter extends
JavaDslModelWriterSupport {
doWriteValue(sb, def.getExpression());
}
protected void doWriteXMLTokenizerExpression(StringBuilder sb,
XMLTokenizerExpression def) {
- doWriteSingleInputTypedExpressionDefinitionAttributes(sb, def);
+ doWriteNamespaceAwareExpressionAttributes(sb, def);
doWriteAttribute(sb, "mode", def.getMode(), "i");
doWriteAttribute(sb, "group", def.getGroup(), null);
doWriteValue(sb, def.getExpression());
doWriteNamespaceAwareExpressionElements(sb, def);
}
protected void doWriteXPathExpression(StringBuilder sb, XPathExpression
def) {
- doWriteSingleInputTypedExpressionDefinitionAttributes(sb, def);
+ doWriteNamespaceAwareExpressionAttributes(sb, def);
doWriteAttribute(sb, "documentType", def.getDocumentTypeName(), null);
doWriteAttribute(sb, "resultQName", def.getResultQName(), "NODESET");
doWriteAttribute(sb, "saxon", def.getSaxon(), null);
@@ -3643,7 +3647,7 @@ public class JavaDslModelWriter extends
JavaDslModelWriterSupport {
doWriteNamespaceAwareExpressionElements(sb, def);
}
protected void doWriteXQueryExpression(StringBuilder sb, XQueryExpression
def) {
- doWriteSingleInputTypedExpressionDefinitionAttributes(sb, def);
+ doWriteNamespaceAwareExpressionAttributes(sb, def);
doWriteAttribute(sb, "configurationRef", def.getConfigurationRef(),
null);
doWriteValue(sb, def.getExpression());
doWriteNamespaceAwareExpressionElements(sb, def);
diff --git
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index d2f9e108d076..ee55786610c3 100644
---
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -2379,9 +2379,15 @@ public class ModelParser extends BaseParser {
return doParse(new XMLTokenizerExpression(), (def, key, val) -> switch
(key) {
case "group": def.setGroup(val); yield true;
case "mode": def.setMode(val); yield true;
- default: yield
singleInputTypedExpressionDefinitionAttributeHandler().accept(def, key, val);
+ default: yield
namespaceAwareExpressionAttributeHandler().accept(def, key, val);
}, namespaceAwareExpressionElementHandler(),
expressionDefinitionValueHandler());
}
+ protected <T extends NamespaceAwareExpression> AttributeHandler<T>
namespaceAwareExpressionAttributeHandler() {
+ return (def, key, val) -> switch (key) {
+ case "namespacesRef": def.setNamespacesRef(val); yield true;
+ default: yield
singleInputTypedExpressionDefinitionAttributeHandler().accept(def, key, val);
+ };
+ }
protected <T extends NamespaceAwareExpression> ElementHandler<T>
namespaceAwareExpressionElementHandler() {
return (def, key) -> switch (key) {
case "namespace": doAdd(doParsePropertyDefinition(),
def.getNamespace(), def::setNamespace); yield true;
@@ -2398,13 +2404,13 @@ public class ModelParser extends BaseParser {
case "resultQName": def.setResultQName(val); yield true;
case "saxon": def.setSaxon(val); yield true;
case "threadSafety": def.setThreadSafety(val); yield true;
- default: yield
singleInputTypedExpressionDefinitionAttributeHandler().accept(def, key, val);
+ default: yield
namespaceAwareExpressionAttributeHandler().accept(def, key, val);
}, namespaceAwareExpressionElementHandler(),
expressionDefinitionValueHandler());
}
protected XQueryExpression doParseXQueryExpression() throws IOException,
XmlPullParserException {
return doParse(new XQueryExpression(), (def, key, val) -> switch (key)
{
case "configurationRef": def.setConfigurationRef(val); yield
true;
- default: yield
singleInputTypedExpressionDefinitionAttributeHandler().accept(def, key, val);
+ default: yield
namespaceAwareExpressionAttributeHandler().accept(def, key, val);
}, namespaceAwareExpressionElementHandler(),
expressionDefinitionValueHandler());
}
protected CustomLoadBalancerDefinition
doParseCustomLoadBalancerDefinition() throws IOException,
XmlPullParserException {
diff --git
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 3e120b4988b6..89e3dfb53576 100644
---
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -3001,12 +3001,16 @@ public class ModelWriter extends BaseWriter {
doWriteValue(def.getExpression());
endElement(name);
}
+ protected void
doWriteNamespaceAwareExpressionAttributes(NamespaceAwareExpression def) throws
IOException {
+ doWriteSingleInputTypedExpressionDefinitionAttributes(def);
+ doWriteAttribute("namespacesRef", def.getNamespacesRef(), null);
+ }
protected void
doWriteNamespaceAwareExpressionElements(NamespaceAwareExpression def) throws
IOException {
doWriteList(null, "namespace", def.getNamespace(),
this::doWritePropertyDefinition);
}
protected void doWriteNamespaceAwareExpression(String name,
NamespaceAwareExpression def) throws IOException {
startElement(name);
- doWriteSingleInputTypedExpressionDefinitionAttributes(def);
+ doWriteNamespaceAwareExpressionAttributes(def);
doWriteValue(def.getExpression());
doWriteNamespaceAwareExpressionElements(def);
endElement(name);
@@ -3106,7 +3110,7 @@ public class ModelWriter extends BaseWriter {
}
protected void doWriteXMLTokenizerExpression(String name,
XMLTokenizerExpression def) throws IOException {
startElement(name);
- doWriteSingleInputTypedExpressionDefinitionAttributes(def);
+ doWriteNamespaceAwareExpressionAttributes(def);
doWriteAttribute("mode", def.getMode(), "i");
doWriteAttribute("group", def.getGroup(), null);
doWriteValue(def.getExpression());
@@ -3115,7 +3119,7 @@ public class ModelWriter extends BaseWriter {
}
protected void doWriteXPathExpression(String name, XPathExpression def)
throws IOException {
startElement(name);
- doWriteSingleInputTypedExpressionDefinitionAttributes(def);
+ doWriteNamespaceAwareExpressionAttributes(def);
doWriteAttribute("documentType", def.getDocumentTypeName(), null);
doWriteAttribute("resultQName", def.getResultQName(), "NODESET");
doWriteAttribute("saxon", def.getSaxon(), null);
@@ -3130,7 +3134,7 @@ public class ModelWriter extends BaseWriter {
}
protected void doWriteXQueryExpression(String name, XQueryExpression def)
throws IOException {
startElement(name);
- doWriteSingleInputTypedExpressionDefinitionAttributes(def);
+ doWriteNamespaceAwareExpressionAttributes(def);
doWriteAttribute("configurationRef", def.getConfigurationRef(), null);
doWriteValue(def.getExpression());
doWriteNamespaceAwareExpressionElements(def);
diff --git
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
index fc56d46bb007..9fbb01f51fb6 100644
---
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
+++
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
@@ -2999,12 +2999,16 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
doWriteValue(jo, def.getExpression());
return jo;
}
+ protected void doWriteNamespaceAwareExpressionAttributes(JsonObject jo,
NamespaceAwareExpression def) {
+ doWriteSingleInputTypedExpressionDefinitionAttributes(jo, def);
+ doWriteAttribute(jo, "namespacesRef", def.getNamespacesRef(), null);
+ }
protected void doWriteNamespaceAwareExpressionElements(JsonObject jo,
NamespaceAwareExpression def) {
doWriteChildList(jo, null, "namespace", def.getNamespace(),
this::doWritePropertyDefinition);
}
protected JsonObject
doWriteNamespaceAwareExpression(NamespaceAwareExpression def) {
JsonObject jo = new JsonObject();
- doWriteSingleInputTypedExpressionDefinitionAttributes(jo, def);
+ doWriteNamespaceAwareExpressionAttributes(jo, def);
doWriteValue(jo, def.getExpression());
doWriteNamespaceAwareExpressionElements(jo, def);
return jo;
@@ -3104,7 +3108,7 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
}
protected JsonObject doWriteXMLTokenizerExpression(XMLTokenizerExpression
def) {
JsonObject jo = new JsonObject();
- doWriteSingleInputTypedExpressionDefinitionAttributes(jo, def);
+ doWriteNamespaceAwareExpressionAttributes(jo, def);
doWriteAttribute(jo, "mode", def.getMode(), "i");
doWriteAttribute(jo, "group", def.getGroup(), null);
doWriteValue(jo, def.getExpression());
@@ -3113,7 +3117,7 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
}
protected JsonObject doWriteXPathExpression(XPathExpression def) {
JsonObject jo = new JsonObject();
- doWriteSingleInputTypedExpressionDefinitionAttributes(jo, def);
+ doWriteNamespaceAwareExpressionAttributes(jo, def);
doWriteAttribute(jo, "documentType", def.getDocumentTypeName(), null);
doWriteAttribute(jo, "resultQName", def.getResultQName(), "NODESET");
doWriteAttribute(jo, "saxon", def.getSaxon(), null);
@@ -3128,7 +3132,7 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
}
protected JsonObject doWriteXQueryExpression(XQueryExpression def) {
JsonObject jo = new JsonObject();
- doWriteSingleInputTypedExpressionDefinitionAttributes(jo, def);
+ doWriteNamespaceAwareExpressionAttributes(jo, def);
doWriteAttribute(jo, "configurationRef", def.getConfigurationRef(),
null);
doWriteValue(jo, def.getExpression());
doWriteNamespaceAwareExpressionElements(jo, def);
diff --git
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlXPathNamespacesRefTest.java
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlXPathNamespacesRefTest.java
new file mode 100644
index 000000000000..4c01b3501008
--- /dev/null
+++
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlXPathNamespacesRefTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.dsl.xml.io;
+
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.support.builder.Namespaces;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that a route loaded from plain XML DSL can reference a {@link
Namespaces} bean from the registry via the xpath
+ * element's namespacesRef attribute, with the bean itself also declared in
XML.
+ */
+class XmlXPathNamespacesRefTest {
+
+ @Test
+ void testXPathNamespacesRefFromXml() throws Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ context.start();
+
+ Resource resource =
ResourceHelper.fromString("xpathNamespacesRef.xml", """
+ <camel xmlns="http://camel.apache.org/schema/xml-io">
+ <bean name="myNamespaces"
type="org.apache.camel.support.builder.Namespaces">
+ <properties>
+ <property key="namespaces[c]"
value="http://acme.com/cheese"/>
+ <property key="namespaces[w]"
value="http://acme.com/wine"/>
+ </properties>
+ </bean>
+ <route id="xpathNamespacesRef">
+ <from uri="direct:xpathNamespacesRef"/>
+ <choice>
+ <when>
+ <xpath resultQName="BOOLEAN"
namespacesRef="myNamespaces">/c:number = 55</xpath>
+ <to uri="mock:cheeseMatch"/>
+ </when>
+ <when>
+ <xpath resultQName="BOOLEAN"
namespacesRef="myNamespaces">/w:number = 77</xpath>
+ <to uri="mock:wineMatch"/>
+ </when>
+ <otherwise>
+ <to uri="mock:noMatch"/>
+ </otherwise>
+ </choice>
+ </route>
+ </camel>
+ """);
+ PluginHelper.getRoutesLoader(context).loadRoutes(resource);
+
+ MockEndpoint cheeseMatch = context.getEndpoint("mock:cheeseMatch",
MockEndpoint.class);
+ MockEndpoint wineMatch = context.getEndpoint("mock:wineMatch",
MockEndpoint.class);
+ MockEndpoint noMatch = context.getEndpoint("mock:noMatch",
MockEndpoint.class);
+
+ // positive example: cheese namespace
+ cheeseMatch.expectedMessageCount(1);
+
context.createProducerTemplate().sendBody("direct:xpathNamespacesRef",
+ "<number xmlns=\"http://acme.com/cheese\">55</number>");
+ cheeseMatch.assertIsSatisfied();
+
+ // positive example: wine namespace
+ wineMatch.expectedMessageCount(1);
+
context.createProducerTemplate().sendBody("direct:xpathNamespacesRef",
+ "<number xmlns=\"http://acme.com/wine\">77</number>");
+ wineMatch.assertIsSatisfied();
+
+ noMatch.expectedMessageCount(2);
+
+ // negative, data-wise: correct namespace, wrong value
+
context.createProducerTemplate().sendBody("direct:xpathNamespacesRef",
+ "<number xmlns=\"http://acme.com/cheese\">99</number>");
+
+ // negative, namespace-wise: correct value, wrong namespace URI
+
context.createProducerTemplate().sendBody("direct:xpathNamespacesRef",
+ "<number xmlns=\"http://acme.com/water\">77</number>");
+
+ noMatch.assertIsSatisfied();
+ }
+ }
+}
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
index 5a40d6b83956..d62465d1a36d 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
@@ -21026,6 +21026,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
@YamlProperty(name = "id", type = "string", description =
"The id of this node.", displayName = "Id"),
@YamlProperty(name = "mode", type = "enum:i,w,u,t",
defaultValue = "i", description = "The extraction mode. The available
extraction modes are: i - injecting the contextual namespace bindings into the
extracted token (default), w - wrapping the extracted token in its ancestor
context, u - unwrapping the extracted token to its child content, t -
extracting the text content of the specified element.", displayName = "Mode"),
@YamlProperty(name = "namespace", type =
"array:org.apache.camel.model.PropertyDefinition", description = "Injects the
XML Namespaces of prefix to uri mappings.", displayName = "Namespace"),
+ @YamlProperty(name = "namespacesRef", type = "string",
description = "Reference to a org.apache.camel.support.builder.Namespaces bean
in the registry to use for the XML Namespaces of prefix to uri mappings.",
displayName = "Namespaces Ref"),
@YamlProperty(name = "resultType", type = "string",
description = "The class of the result type (type from output).", displayName =
"Result Type"),
@YamlProperty(name = "source", type = "string",
description = "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body.", displayName = "Source"),
@YamlProperty(name = "trim", type = "boolean",
defaultValue = "true", description = "Whether to trim the source code to remove
leading and trailing whitespaces and line breaks.", displayName = "Trim")
@@ -21076,6 +21077,11 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
target.setNamespace(val);
break;
}
+ case "namespacesRef": {
+ String val = asText(node);
+ target.setNamespacesRef(val);
+ break;
+ }
case "resultType": {
String val = asText(node);
target.setResultTypeName(val);
@@ -21123,6 +21129,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
@YamlProperty(name = "id", type = "string", description =
"The id of this node.", displayName = "Id"),
@YamlProperty(name = "logNamespaces", type = "boolean",
defaultValue = "false", description = "Whether to log namespaces which can
assist during troubleshooting.", displayName = "Log Namespaces"),
@YamlProperty(name = "namespace", type =
"array:org.apache.camel.model.PropertyDefinition", description = "Injects the
XML Namespaces of prefix to uri mappings.", displayName = "Namespace"),
+ @YamlProperty(name = "namespacesRef", type = "string",
description = "Reference to a org.apache.camel.support.builder.Namespaces bean
in the registry to use for the XML Namespaces of prefix to uri mappings.",
displayName = "Namespaces Ref"),
@YamlProperty(name = "objectModel", type = "string",
description = "The XPath object model to use.", displayName = "Object Model"),
@YamlProperty(name = "preCompile", type = "boolean",
defaultValue = "true", description = "Whether to enable pre-compiling the xpath
expression during initialization phase. pre-compile is enabled by default.",
displayName = "Pre Compile"),
@YamlProperty(name = "resultQName", type =
"enum:NUMBER,STRING,BOOLEAN,NODESET,NODE", defaultValue = "NODESET",
description = "Sets the output type supported by XPath.", displayName = "Result
QName"),
@@ -21183,6 +21190,11 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
target.setNamespace(val);
break;
}
+ case "namespacesRef": {
+ String val = asText(node);
+ target.setNamespacesRef(val);
+ break;
+ }
case "objectModel": {
String val = asText(node);
target.setObjectModel(val);
@@ -21253,6 +21265,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
@YamlProperty(name = "expression", type = "string",
required = true, description = "The expression value in your chosen language
syntax.", displayName = "Expression"),
@YamlProperty(name = "id", type = "string", description =
"The id of this node.", displayName = "Id"),
@YamlProperty(name = "namespace", type =
"array:org.apache.camel.model.PropertyDefinition", description = "Injects the
XML Namespaces of prefix to uri mappings.", displayName = "Namespace"),
+ @YamlProperty(name = "namespacesRef", type = "string",
description = "Reference to a org.apache.camel.support.builder.Namespaces bean
in the registry to use for the XML Namespaces of prefix to uri mappings.",
displayName = "Namespaces Ref"),
@YamlProperty(name = "resultType", type = "string",
description = "The class of the result type (type from output).", displayName =
"Result Type"),
@YamlProperty(name = "source", type = "string",
description = "Source to use, instead of message body. You can prefix with
variable:, header:, or property: to specify kind of source. Otherwise, the
source is assumed to be a variable. Use empty or null to use default source,
which is the message body.", displayName = "Source"),
@YamlProperty(name = "trim", type = "boolean",
defaultValue = "true", description = "Whether to trim the source code to remove
leading and trailing whitespaces and line breaks.", displayName = "Trim")
@@ -21298,6 +21311,11 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
target.setNamespace(val);
break;
}
+ case "namespacesRef": {
+ String val = asText(node);
+ target.setNamespacesRef(val);
+ break;
+ }
case "resultType": {
String val = asText(node);
target.setResultTypeName(val);
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
index 5c915ce5da31..f67f6807d1b7 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
@@ -10576,6 +10576,11 @@
"$ref" :
"#/items/definitions/org.apache.camel.model.PropertyDefinition"
}
},
+ "namespacesRef" : {
+ "type" : "string",
+ "title" : "Namespaces Ref",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings."
+ },
"resultType" : {
"type" : "string",
"title" : "Result Type",
@@ -10635,6 +10640,11 @@
"$ref" :
"#/items/definitions/org.apache.camel.model.PropertyDefinition"
}
},
+ "namespacesRef" : {
+ "type" : "string",
+ "title" : "Namespaces Ref",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings."
+ },
"objectModel" : {
"type" : "string",
"title" : "Object Model",
@@ -10713,6 +10723,11 @@
"$ref" :
"#/items/definitions/org.apache.camel.model.PropertyDefinition"
}
},
+ "namespacesRef" : {
+ "type" : "string",
+ "title" : "Namespaces Ref",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings."
+ },
"resultType" : {
"type" : "string",
"title" : "Result Type",
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
index 2a19bbe7cccc..2a965559ce02 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
@@ -15978,6 +15978,11 @@
"ref" : "property",
"description" : "Injects the XML Namespaces of prefix to uri
mappings.",
"title" : "Namespace"
+ }, {
+ "name" : "namespacesRef",
+ "type" : "string",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings.",
+ "title" : "Namespaces Ref"
}, {
"name" : "resultType",
"type" : "string",
@@ -16032,6 +16037,11 @@
"ref" : "property",
"description" : "Injects the XML Namespaces of prefix to uri
mappings.",
"title" : "Namespace"
+ }, {
+ "name" : "namespacesRef",
+ "type" : "string",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings.",
+ "title" : "Namespaces Ref"
}, {
"name" : "objectModel",
"type" : "string",
@@ -16105,6 +16115,11 @@
"ref" : "property",
"description" : "Injects the XML Namespaces of prefix to uri
mappings.",
"title" : "Namespace"
+ }, {
+ "name" : "namespacesRef",
+ "type" : "string",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings.",
+ "title" : "Namespaces Ref"
}, {
"name" : "resultType",
"type" : "string",
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 9324a3669b58..9f5731ff56af 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -14381,6 +14381,11 @@
"$ref" :
"#/items/definitions/org.apache.camel.model.PropertyDefinition"
}
},
+ "namespacesRef" : {
+ "type" : "string",
+ "title" : "Namespaces Ref",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings."
+ },
"resultType" : {
"type" : "string",
"title" : "Result Type",
@@ -14444,6 +14449,11 @@
"$ref" :
"#/items/definitions/org.apache.camel.model.PropertyDefinition"
}
},
+ "namespacesRef" : {
+ "type" : "string",
+ "title" : "Namespaces Ref",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings."
+ },
"objectModel" : {
"type" : "string",
"title" : "Object Model",
@@ -14526,6 +14536,11 @@
"$ref" :
"#/items/definitions/org.apache.camel.model.PropertyDefinition"
}
},
+ "namespacesRef" : {
+ "type" : "string",
+ "title" : "Namespaces Ref",
+ "description" : "Reference to a
org.apache.camel.support.builder.Namespaces bean in the registry to use for the
XML Namespaces of prefix to uri mappings."
+ },
"resultType" : {
"type" : "string",
"title" : "Result Type",
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/XPathNamespacesRefYamlDslTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/XPathNamespacesRefYamlDslTest.java
new file mode 100644
index 000000000000..5726b286eb07
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/XPathNamespacesRefYamlDslTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.dsl.yaml;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.support.builder.Namespaces;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that a route loaded from YAML DSL can reference a {@link Namespaces}
bean from the registry via the xpath
+ * step's namespacesRef property, with the bean itself also declared in YAML.
+ */
+class XPathNamespacesRefYamlDslTest {
+
+ @Test
+ void loadsXPathWithNamespacesRef() throws Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ YamlRoutesBuilderLoader loader = new YamlRoutesBuilderLoader();
+ loader.setCamelContext(context);
+ RoutesBuilder routesBuilder =
loader.loadRoutesBuilder(ResourceHelper.fromString("xpath-namespaces-ref.yaml",
"""
+ - beans:
+ - name: myNamespaces
+ type: org.apache.camel.support.builder.Namespaces
+ properties:
+ namespaces[c]: "http://acme.com/cheese"
+ namespaces[w]: "http://acme.com/wine"
+ - route:
+ id: xpath-namespaces-ref
+ from:
+ uri: "direct:xpathNamespacesRef"
+ steps:
+ - choice:
+ when:
+ - xpath:
+ expression: "/c:number = 55"
+ resultQName: "BOOLEAN"
+ namespacesRef: "myNamespaces"
+ steps:
+ - to: "mock:cheeseMatch"
+ - xpath:
+ expression: "/w:number = 77"
+ resultQName: "BOOLEAN"
+ namespacesRef: "myNamespaces"
+ steps:
+ - to: "mock:wineMatch"
+ otherwise:
+ steps:
+ - to: "mock:noMatch"
+ """));
+ context.addRoutes(routesBuilder);
+ context.start();
+
+ MockEndpoint cheeseMatch = context.getEndpoint("mock:cheeseMatch",
MockEndpoint.class);
+ MockEndpoint wineMatch = context.getEndpoint("mock:wineMatch",
MockEndpoint.class);
+ MockEndpoint noMatch = context.getEndpoint("mock:noMatch",
MockEndpoint.class);
+
+ // positive example: cheese namespace
+ cheeseMatch.expectedMessageCount(1);
+
context.createProducerTemplate().sendBody("direct:xpathNamespacesRef",
+ "<number xmlns=\"http://acme.com/cheese\">55</number>");
+ cheeseMatch.assertIsSatisfied();
+
+ // positive example: wine namespace
+ wineMatch.expectedMessageCount(1);
+
context.createProducerTemplate().sendBody("direct:xpathNamespacesRef",
+ "<number xmlns=\"http://acme.com/wine\">77</number>");
+ wineMatch.assertIsSatisfied();
+
+ noMatch.expectedMessageCount(2);
+
+ // negative, data-wise: correct namespace, wrong value
+
context.createProducerTemplate().sendBody("direct:xpathNamespacesRef",
+ "<number xmlns=\"http://acme.com/cheese\">99</number>");
+
+ // negative, namespace-wise: correct value, wrong namespace URI
+
context.createProducerTemplate().sendBody("direct:xpathNamespacesRef",
+ "<number xmlns=\"http://acme.com/water\">77</number>");
+
+ noMatch.assertIsSatisfied();
+ }
+ }
+}