This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new bc6ffc52254 CAMEL-22361: camel-groovy-xml - Add data format that uses 
groovy xml that is easy to use and no need for POJOs
bc6ffc52254 is described below

commit bc6ffc522540afff04340902cdec610ac375b14b
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Aug 22 17:16:42 2025 +0200

    CAMEL-22361: camel-groovy-xml - Add data format that uses groovy xml that 
is easy to use and no need for POJOs
---
 .../camel/catalog/dataformats/groovyXml.json       |  5 +-
 .../org/apache/camel/catalog/models/groovyXml.json |  3 +-
 .../apache/camel/catalog/schemas/camel-spring.xsd  | 10 ++++
 .../apache/camel/catalog/schemas/camel-xml-io.xsd  | 10 ++++
 .../groovy/xml/GroovyXmlDataFormatConfigurer.java  | 21 ++++++-
 .../org/apache/camel/groovy/xml/groovyXml.json     |  3 +-
 .../camel/groovy/xml/GroovyXmlDataFormat.java      | 69 ++++++++++++++++++----
 .../camel/groovy/xml/GroovyXmlDataFormatTest.java  | 57 ++++++++++++++++++
 .../apache/camel/model/dataformat/groovyXml.json   |  3 +-
 .../model/dataformat/GroovyXmlDataFormat.java      | 45 ++++++++++++++
 .../dataformat/GroovyXmlDataFormatReifier.java     |  2 +-
 .../java/org/apache/camel/xml/in/ModelParser.java  |  5 +-
 .../java/org/apache/camel/xml/out/ModelWriter.java |  1 +
 .../org/apache/camel/yaml/out/ModelWriter.java     |  1 +
 .../dsl/yaml/deserializers/ModelDeserializers.java | 10 +++-
 .../generated/resources/schema/camelYamlDsl.json   |  5 ++
 16 files changed, 229 insertions(+), 21 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/groovyXml.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/groovyXml.json
index af52a11b703..f7d93843c94 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/groovyXml.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/groovyXml.json
@@ -3,7 +3,7 @@
     "kind": "dataformat",
     "name": "groovyXml",
     "title": "Groovy XML",
-    "description": "Transform between XML and Java Map via Groovy",
+    "description": "Transform between XML and Groovy Node (Map structure) 
objects.",
     "deprecated": false,
     "firstVersion": "4.15.0",
     "label": "dataformat,transformation,xml",
@@ -16,6 +16,7 @@
     "modelJavaType": "org.apache.camel.model.dataformat.GroovyXmlDataFormat"
   },
   "properties": {
-    "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": 
"common", "required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
id of this node" }
+    "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": 
"common", "required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
id of this node" },
+    "attributeMapping": { "index": 1, "kind": "attribute", "displayName": 
"Attribute Mapping", "group": "common", "required": false, "type": "boolean", 
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": true, "description": "To turn on or off 
attribute mapping. When enabled then keys that start with _ or character will 
be mapped to an XML attribute, and vise versa. This rule is what Jackson and 
other XML or JSon libraries uses." }
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/groovyXml.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/groovyXml.json
index b4752eec3eb..18f3b49f8ef 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/groovyXml.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/groovyXml.json
@@ -13,6 +13,7 @@
     "output": false
   },
   "properties": {
-    "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": 
"common", "required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
id of this node" }
+    "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": 
"common", "required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
id of this node" },
+    "attributeMapping": { "index": 1, "kind": "attribute", "displayName": 
"Attribute Mapping", "group": "common", "required": false, "type": "boolean", 
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": true, "description": "To turn on or off 
attribute mapping. When enabled then keys that start with _ or character will 
be mapped to an XML attribute, and vise versa. This rule is what Jackson and 
other XML or JSon libraries uses." }
   }
 }
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 7fe2161db41..aef6e3a6bfa 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
@@ -8950,6 +8950,16 @@ Whether to capture named expressions only or not (i.e. 
%{IP:ip} but not ${IP}).
     <xs:complexContent>
       <xs:extension base="tns:dataFormat">
         <xs:sequence/>
+        <xs:attribute name="attributeMapping" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To turn on or off attribute mapping. When enabled then keys that start with _ 
or character will be mapped to an XML
+attribute, and vise versa. This rule is what Jackson and other XML or JSon 
libraries uses. Default value: true
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
       </xs:extension>
     </xs:complexContent>
   </xs:complexType>
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 44b56d3fe1e..5962628e4a0 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
@@ -7623,6 +7623,16 @@ Whether to capture named expressions only or not (i.e. 
%{IP:ip} but not ${IP}).
     <xs:complexContent>
       <xs:extension base="tns:dataFormat">
         <xs:sequence/>
+        <xs:attribute name="attributeMapping" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To turn on or off attribute mapping. When enabled then keys that start with _ 
or character will be mapped to an XML
+attribute, and vise versa. This rule is what Jackson and other XML or JSon 
libraries uses. Default value: true
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
       </xs:extension>
     </xs:complexContent>
   </xs:complexType>
diff --git 
a/components/camel-groovy-xml/src/generated/java/org/apache/camel/groovy/xml/GroovyXmlDataFormatConfigurer.java
 
b/components/camel-groovy-xml/src/generated/java/org/apache/camel/groovy/xml/GroovyXmlDataFormatConfigurer.java
index b7e8286cc5c..f7f9c38e128 100644
--- 
a/components/camel-groovy-xml/src/generated/java/org/apache/camel/groovy/xml/GroovyXmlDataFormatConfigurer.java
+++ 
b/components/camel-groovy-xml/src/generated/java/org/apache/camel/groovy/xml/GroovyXmlDataFormatConfigurer.java
@@ -22,12 +22,18 @@ public class GroovyXmlDataFormatConfigurer extends 
org.apache.camel.support.comp
     private static final Map<String, Object> ALL_OPTIONS;
     static {
         Map<String, Object> map = new CaseInsensitiveMap();
+        map.put("AttributeMapping", boolean.class);
         ALL_OPTIONS = map;
     }
 
     @Override
     public boolean configure(CamelContext camelContext, Object obj, String 
name, Object value, boolean ignoreCase) {
-        return false;
+        GroovyXmlDataFormat target = (GroovyXmlDataFormat) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "attributemapping":
+        case "attributeMapping": 
target.setAttributeMapping(property(camelContext, boolean.class, value)); 
return true;
+        default: return false;
+        }
     }
 
     @Override
@@ -37,12 +43,21 @@ public class GroovyXmlDataFormatConfigurer extends 
org.apache.camel.support.comp
 
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
-        return null;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "attributemapping":
+        case "attributeMapping": return boolean.class;
+        default: return null;
+        }
     }
 
     @Override
     public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
-        return null;
+        GroovyXmlDataFormat target = (GroovyXmlDataFormat) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "attributemapping":
+        case "attributeMapping": return target.isAttributeMapping();
+        default: return null;
+        }
     }
 }
 
diff --git 
a/components/camel-groovy-xml/src/generated/resources/META-INF/org/apache/camel/groovy/xml/groovyXml.json
 
b/components/camel-groovy-xml/src/generated/resources/META-INF/org/apache/camel/groovy/xml/groovyXml.json
index 20da0954eab..f7d93843c94 100644
--- 
a/components/camel-groovy-xml/src/generated/resources/META-INF/org/apache/camel/groovy/xml/groovyXml.json
+++ 
b/components/camel-groovy-xml/src/generated/resources/META-INF/org/apache/camel/groovy/xml/groovyXml.json
@@ -16,6 +16,7 @@
     "modelJavaType": "org.apache.camel.model.dataformat.GroovyXmlDataFormat"
   },
   "properties": {
-    "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": 
"common", "required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
id of this node" }
+    "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": 
"common", "required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
id of this node" },
+    "attributeMapping": { "index": 1, "kind": "attribute", "displayName": 
"Attribute Mapping", "group": "common", "required": false, "type": "boolean", 
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": true, "description": "To turn on or off 
attribute mapping. When enabled then keys that start with _ or character will 
be mapped to an XML attribute, and vise versa. This rule is what Jackson and 
other XML or JSon libraries uses." }
   }
 }
diff --git 
a/components/camel-groovy-xml/src/main/java/org/apache/camel/groovy/xml/GroovyXmlDataFormat.java
 
b/components/camel-groovy-xml/src/main/java/org/apache/camel/groovy/xml/GroovyXmlDataFormat.java
index d29d19e4882..fe98d581828 100644
--- 
a/components/camel-groovy-xml/src/main/java/org/apache/camel/groovy/xml/GroovyXmlDataFormat.java
+++ 
b/components/camel-groovy-xml/src/main/java/org/apache/camel/groovy/xml/GroovyXmlDataFormat.java
@@ -20,8 +20,10 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.StringJoiner;
 
 import groovy.util.Node;
 import groovy.xml.XmlNodePrinter;
@@ -43,6 +45,16 @@ public class GroovyXmlDataFormat extends ServiceSupport 
implements DataFormat, D
     private static final int VALUE = 2;
     private static final int END_TAG = 3;
 
+    private boolean attributeMapping = true;
+
+    public boolean isAttributeMapping() {
+        return attributeMapping;
+    }
+
+    public void setAttributeMapping(boolean attributeMapping) {
+        this.attributeMapping = attributeMapping;
+    }
+
     @Override
     public void marshal(Exchange exchange, Object graph, OutputStream stream) 
throws Exception {
         if (graph instanceof GPathResult gp) {
@@ -83,7 +95,7 @@ public class GroovyXmlDataFormat extends ServiceSupport 
implements DataFormat, D
 
     private void printLines(List<Line> lines, OutputStream os) throws 
Exception {
         // add missing root end tag
-        lines.add(new Line(lines.get(0).key, null, END_TAG));
+        lines.add(new Line(lines.get(0).key, null, END_TAG, null));
         int level = 0;
         for (Line line : lines) {
             int kind = line.kind;
@@ -92,6 +104,16 @@ public class GroovyXmlDataFormat extends ServiceSupport 
implements DataFormat, D
                 os.write(pad.getBytes());
                 os.write("<".getBytes());
                 os.write(line.key.getBytes());
+                if (line.attrs != null) {
+                    StringJoiner sj = new StringJoiner(" ");
+                    for (var a : line.attrs.entrySet()) {
+                        sj.add(a.getKey() + "=\"" + a.getValue() + "\"");
+                    }
+                    if (sj.length() > 0) {
+                        os.write(" ".getBytes());
+                        os.write(sj.toString().getBytes());
+                    }
+                }
                 os.write(">\n".getBytes());
                 level++;
             } else if (kind == END_TAG) {
@@ -122,19 +144,45 @@ public class GroovyXmlDataFormat extends ServiceSupport 
implements DataFormat, D
     }
 
     private void doSerialize(CamelContext context, Map<String, Object> map, 
List<Line> lines) {
+
+        // attributes
+        Map<String, String> attrs = new LinkedHashMap<>();
+        if (attributeMapping) {
+            for (String key : map.keySet()) {
+                if (key.startsWith("_") || key.startsWith("@")) {
+                    String val = 
context.getTypeConverter().convertTo(String.class, map.get(key));
+                    if (val != null) {
+                        val = val.trim();
+                        if (!val.isBlank()) {
+                            attrs.put(key.substring(1), val);
+                        }
+                    }
+                }
+            }
+        }
+
         boolean root = false;
         for (var e : map.entrySet()) {
             String key = e.getKey();
 
-            // attributes are not yet supported
-            if (key.startsWith("_")) {
+            // attribute mappings are disabled
+            if (key.startsWith("_") || key.startsWith("@")) {
                 continue;
             }
 
+            if (!attrs.isEmpty() && !lines.isEmpty()) {
+                int pos = lines.size() - 1;
+                Line prev = lines.get(pos);
+                lines.remove(pos);
+                Line updated = new Line(prev.key, prev.value, prev.kind, new 
LinkedHashMap<>(attrs));
+                lines.add(updated);
+                attrs.clear();
+            }
+
             // root tag
             if (lines.isEmpty()) {
                 root = true;
-                lines.add(new Line(key, null, START_TAG));
+                lines.add(new Line(key, null, START_TAG, null));
             }
 
             if (e.getValue() != null) {
@@ -142,13 +190,13 @@ public class GroovyXmlDataFormat extends ServiceSupport 
implements DataFormat, D
                 if (e.getValue() instanceof Map cm) {
                     doSerialize(context, cm, lines);
                 } else if (e.getValue() instanceof List cl) {
-                    doSerialize(context, cl, key, lines, root);
+                    doSerialize(context, cl, key, attrs, lines, root);
                 } else {
                     String val = 
context.getTypeConverter().convertTo(String.class, e.getValue());
                     if (val != null) {
                         val = val.trim();
                         if (!val.isBlank()) {
-                            lines.add(new Line(key, val, VALUE));
+                            lines.add(new Line(key, val, VALUE, null));
                         }
                     }
                 }
@@ -156,21 +204,22 @@ public class GroovyXmlDataFormat extends ServiceSupport 
implements DataFormat, D
         }
     }
 
-    private void doSerialize(CamelContext context, List list, String key, 
List<Line> lines, boolean root) {
+    private void doSerialize(
+            CamelContext context, List list, String key, Map<String, String> 
attrs, List<Line> lines, boolean root) {
         for (var e : list) {
             if (!root) {
-                lines.add(new Line(key, null, START_TAG));
+                lines.add(new Line(key, null, START_TAG, attrs));
             }
             if (e instanceof Map map) {
                 doSerialize(context, map, lines);
             }
             if (!root) {
-                lines.add(new Line(key, null, END_TAG));
+                lines.add(new Line(key, null, END_TAG, null));
             }
         }
     }
 
-    record Line(String key, String value, int kind) {
+    record Line(String key, String value, int kind, Map<String, String> attrs) 
{
     }
 
 }
diff --git 
a/components/camel-groovy-xml/src/test/java/org/apache/camel/groovy/xml/GroovyXmlDataFormatTest.java
 
b/components/camel-groovy-xml/src/test/java/org/apache/camel/groovy/xml/GroovyXmlDataFormatTest.java
index 59ef9b44ba3..9ebaecc9332 100644
--- 
a/components/camel-groovy-xml/src/test/java/org/apache/camel/groovy/xml/GroovyXmlDataFormatTest.java
+++ 
b/components/camel-groovy-xml/src/test/java/org/apache/camel/groovy/xml/GroovyXmlDataFormatTest.java
@@ -192,12 +192,14 @@ public class GroovyXmlDataFormatTest extends 
CamelTestSupport {
         b1.put("author", "F. Scott Fitzgerald");
         b1.put("year", 1925);
         b1.put("genre", "Classic");
+        b1.put("@id", "bk101");
 
         JsonObject b2 = new JsonObject();
         b2.put("title", "1984");
         b2.put("author", "George Orwell");
         b2.put("year", 1949);
         b2.put("genre", "Dystopian");
+        b2.put("@id", "bk102");
 
         arr.add(b1);
         arr.add(b2);
@@ -209,6 +211,40 @@ public class GroovyXmlDataFormatTest extends 
CamelTestSupport {
         Assertions.assertNotNull(out);
         Assertions.assertInstanceOf(byte[].class, out);
 
+        String xml = context.getTypeConverter().convertTo(String.class, out);
+        Assertions.assertEquals(BOOKS, xml);
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    public void testMarshalCamelJSonNoAttr() throws Exception {
+        getMockEndpoint("mock:marshal").expectedMessageCount(1);
+
+        JsonObject root = new JsonObject();
+        JsonArray arr = new JsonArray();
+        JsonObject b1 = new JsonObject();
+        b1.put("title", "No Title");
+        b1.put("author", "F. Scott Fitzgerald");
+        b1.put("year", 1925);
+        b1.put("genre", "Classic");
+
+        JsonObject b2 = new JsonObject();
+        b2.put("title", "1984");
+        b2.put("author", "George Orwell");
+        b2.put("year", 1949);
+        b2.put("genre", "Dystopian");
+
+        arr.add(b1);
+        arr.add(b2);
+        JsonObject books = new JsonObject();
+        books.put("book", arr);
+        root.put("library", books);
+
+        Object out = template.requestBody("direct:marshalNoAttr", root);
+        Assertions.assertNotNull(out);
+        Assertions.assertInstanceOf(byte[].class, out);
+
         String xml = context.getTypeConverter().convertTo(String.class, out);
         Assertions.assertEquals(BOOKS_NO_ATTR, xml);
 
@@ -226,6 +262,23 @@ public class GroovyXmlDataFormatTest extends 
CamelTestSupport {
         Assertions.assertNotNull(out);
         Assertions.assertInstanceOf(byte[].class, out);
 
+        String xml = context.getTypeConverter().convertTo(String.class, out);
+        Assertions.assertEquals(BOOKS, xml);
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    public void testMarshalJacksonJSonNoAttr() throws Exception {
+        getMockEndpoint("mock:marshal").expectedMessageCount(1);
+
+        ObjectMapper om = new JsonMapper();
+        JsonNode root = om.readTree(BOOKS_JSON);
+
+        Object out = template.requestBody("direct:marshalNoAttr", root);
+        Assertions.assertNotNull(out);
+        Assertions.assertInstanceOf(byte[].class, out);
+
         String xml = context.getTypeConverter().convertTo(String.class, out);
         Assertions.assertEquals(BOOKS_NO_ATTR, xml);
 
@@ -277,6 +330,10 @@ public class GroovyXmlDataFormatTest extends 
CamelTestSupport {
                         .marshal().groovyXml()
                         .to("mock:marshal");
 
+                from("direct:marshalNoAttr").streamCache(false)
+                        
.marshal(dataFormat().groovyXml().attributeMapping(false).end())
+                        .to("mock:marshal");
+
                 from("direct:unmarshal")
                         .unmarshal().groovyXml()
                         .to("mock:unmarshal");
diff --git 
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/groovyXml.json
 
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/groovyXml.json
index b4752eec3eb..18f3b49f8ef 100644
--- 
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/groovyXml.json
+++ 
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/groovyXml.json
@@ -13,6 +13,7 @@
     "output": false
   },
   "properties": {
-    "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": 
"common", "required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
id of this node" }
+    "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": 
"common", "required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
id of this node" },
+    "attributeMapping": { "index": 1, "kind": "attribute", "displayName": 
"Attribute Mapping", "group": "common", "required": false, "type": "boolean", 
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": true, "description": "To turn on or off 
attribute mapping. When enabled then keys that start with _ or character will 
be mapped to an XML attribute, and vise versa. This rule is what Jackson and 
other XML or JSon libraries uses." }
   }
 }
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/GroovyXmlDataFormat.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/GroovyXmlDataFormat.java
index a7bd3a117db..448c3fc8d17 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/GroovyXmlDataFormat.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/GroovyXmlDataFormat.java
@@ -18,6 +18,7 @@ package org.apache.camel.model.dataformat;
 
 import jakarta.xml.bind.annotation.XmlAccessType;
 import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlAttribute;
 import jakarta.xml.bind.annotation.XmlRootElement;
 import jakarta.xml.bind.annotation.XmlTransient;
 
@@ -33,16 +34,22 @@ import org.apache.camel.spi.Metadata;
 @XmlAccessorType(XmlAccessType.FIELD)
 public class GroovyXmlDataFormat extends DataFormatDefinition {
 
+    @XmlAttribute
+    @Metadata(javaType = "java.lang.Boolean", defaultValue = "true")
+    private String attributeMapping;
+
     public GroovyXmlDataFormat() {
         super("groovyXml");
     }
 
     protected GroovyXmlDataFormat(GroovyXmlDataFormat source) {
         super(source);
+        this.attributeMapping = source.attributeMapping;
     }
 
     private GroovyXmlDataFormat(Builder builder) {
         this();
+        this.attributeMapping = builder.attributeMapping;
     }
 
     @Override
@@ -50,12 +57,50 @@ public class GroovyXmlDataFormat extends 
DataFormatDefinition {
         return new GroovyXmlDataFormat(this);
     }
 
+    public String getAttributeMapping() {
+        return attributeMapping;
+    }
+
+    /**
+     * To turn on or off attribute mapping. When enabled then keys that start 
with _ or @ character will
+     * be mapped to an XML attribute, and vise versa. This rule is what 
Jackson and other XML or JSon libraries uses.
+     */
+    public void setAttributeMapping(String attributeMapping) {
+        this.attributeMapping = attributeMapping;
+    }
+
+    @Override
+    public String toString() {
+        return "GroovyXmlDataFormat'";
+    }
+
     @XmlTransient
     public static class Builder implements 
DataFormatBuilder<GroovyXmlDataFormat> {
 
+        private String attributeMapping;
+
+        /**
+         * To turn on or off attribute mapping. When enabled then keys that 
start with _ or @ character will
+         * be mapped to an XML attribute, and vise versa. This rule is what 
Jackson and other XML or JSon libraries uses.
+         */
+        public Builder attributeMapping(String attributeMapping) {
+            this.attributeMapping = attributeMapping;
+            return this;
+        }
+
+        /**
+         * To turn on or off attribute mapping. When enabled then keys that 
start with _ or @ character will
+         * be mapped to an XML attribute, and vise versa. This rule is what 
Jackson and other XML or JSon libraries uses.
+         */
+        public Builder attributeMapping(boolean attributeMapping) {
+            this.attributeMapping = Boolean.toString(attributeMapping);
+            return this;
+        }
+
         @Override
         public GroovyXmlDataFormat end() {
             return new GroovyXmlDataFormat(this);
         }
+
     }
 }
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/GroovyXmlDataFormatReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/GroovyXmlDataFormatReifier.java
index 9a0c7186ef1..f0cc6d236ca 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/GroovyXmlDataFormatReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/GroovyXmlDataFormatReifier.java
@@ -30,7 +30,7 @@ public class GroovyXmlDataFormatReifier extends 
DataFormatReifier<GroovyXmlDataF
 
     @Override
     protected void prepareDataFormatConfig(Map<String, Object> properties) {
-        // noop
+        properties.put("attributeMapping", definition.getAttributeMapping());
     }
 
 }
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 a575ff497ea..143322315cb 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
@@ -1951,7 +1951,10 @@ public class ModelParser extends BaseParser {
             }, noElementHandler(), noValueHandler());
     }
     protected GroovyXmlDataFormat doParseGroovyXmlDataFormat() throws 
IOException, XmlPullParserException {
-        return doParse(new GroovyXmlDataFormat(), 
identifiedTypeAttributeHandler(), noElementHandler(), noValueHandler());
+        return doParse(new GroovyXmlDataFormat(), (def, key, val) -> switch 
(key) {
+                case "attributeMapping": def.setAttributeMapping(val); yield 
true;
+                default: yield identifiedTypeAttributeHandler().accept(def, 
key, val);
+            }, noElementHandler(), noValueHandler());
     }
     protected GzipDeflaterDataFormat doParseGzipDeflaterDataFormat() throws 
IOException, XmlPullParserException {
         return doParse(new GzipDeflaterDataFormat(), 
identifiedTypeAttributeHandler(), noElementHandler(), noValueHandler());
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 6a10dc289b9..9bd654d3b53 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
@@ -2651,6 +2651,7 @@ public class ModelWriter extends BaseWriter {
     protected void doWriteGroovyXmlDataFormat(String name, GroovyXmlDataFormat 
def) throws IOException {
         startElement(name);
         doWriteIdentifiedTypeAttributes(def);
+        doWriteAttribute("attributeMapping", def.getAttributeMapping(), 
"true");
         endElement(name);
     }
     protected void doWriteGzipDeflaterDataFormat(String name, 
GzipDeflaterDataFormat def) throws IOException {
diff --git 
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
 
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index 16a2c0b665b..9b1ce4d935c 100644
--- 
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ 
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -2651,6 +2651,7 @@ public class ModelWriter extends BaseWriter {
     protected void doWriteGroovyXmlDataFormat(String name, GroovyXmlDataFormat 
def) throws IOException {
         startElement(name);
         doWriteIdentifiedTypeAttributes(def);
+        doWriteAttribute("attributeMapping", def.getAttributeMapping(), 
"true");
         endElement(name);
     }
     protected void doWriteGzipDeflaterDataFormat(String name, 
GzipDeflaterDataFormat def) throws IOException {
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 15b529e827e..2af34b7b81b 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
@@ -6691,7 +6691,10 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
             displayName = "Groovy XML",
             description = "Transform between XML and Groovy Node (Map 
structure) objects.",
             deprecated = false,
-            properties = @YamlProperty(name = "id", type = "string", 
description = "The id of this node", displayName = "Id")
+            properties = {
+                    @YamlProperty(name = "attributeMapping", type = "boolean", 
description = "To turn on or off attribute mapping. When enabled then keys that 
start with _ or character will be mapped to an XML attribute, and vise versa. 
This rule is what Jackson and other XML or JSon libraries uses.", displayName = 
"Attribute Mapping"),
+                    @YamlProperty(name = "id", type = "string", description = 
"The id of this node", displayName = "Id")
+            }
     )
     public static class GroovyXmlDataFormatDeserializer extends 
YamlDeserializerBase<GroovyXmlDataFormat> {
         public GroovyXmlDataFormatDeserializer() {
@@ -6708,6 +6711,11 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                 String propertyName, Node node) {
             propertyKey = 
org.apache.camel.util.StringHelper.dashToCamelCase(propertyKey);
             switch(propertyKey) {
+                case "attributeMapping": {
+                    String val = asText(node);
+                    target.setAttributeMapping(val);
+                    break;
+                }
                 case "id": {
                     String val = asText(node);
                     target.setId(val);
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 97486a3490e..727a2006585 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
@@ -11119,6 +11119,11 @@
         "type" : "object",
         "additionalProperties" : false,
         "properties" : {
+          "attributeMapping" : {
+            "type" : "boolean",
+            "title" : "Attribute Mapping",
+            "description" : "To turn on or off attribute mapping. When enabled 
then keys that start with _ or character will be mapped to an XML attribute, 
and vise versa. This rule is what Jackson and other XML or JSon libraries uses."
+          },
           "id" : {
             "type" : "string",
             "title" : "Id",

Reply via email to