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

davsclaus pushed a commit to branch fix/CAMEL-25255
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 57075694ad066fd7e13cb8bc0344feefcbee050a
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Oct 2 08:50:48 2026 +0200

    CAMEL-25255: camel-yaml-io - rests, route templates and bean properties are 
written as the YAML DSL loads them
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../org/apache/camel/yaml/out/YamlModelWriter.java |  16 +-
 .../org/apache/camel/yaml/LwModelToYAMLDumper.java |  73 ++-----
 .../camel/yaml/out/YamlModelWriterSupport.java     |  32 +++
 .../apache/camel/yaml/out/YamlBeansDumpTest.java   |  74 +++++++
 .../src/test/resources/yaml-beans-with-route.yaml  |   4 +-
 .../src/test/resources/yaml-beans.yaml             |   7 +-
 .../src/test/resources/yaml-rest.yaml              |   3 +-
 .../src/test/resources/yaml-route-template.yaml    |   2 +-
 .../src/test/resources/blueprint-out.yaml          |   2 +-
 .../validator/YamlRestTemplateRoundTripTest.java   | 221 +++++++++++++++++++++
 .../main/resources/velocity/model-yaml-writer.vm   |  19 +-
 11 files changed, 376 insertions(+), 77 deletions(-)

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 55f7b50c48ca..3aeb60ef3950 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
@@ -870,8 +870,8 @@ public class YamlModelWriter extends YamlModelWriterSupport 
{
         doWriteAttribute(jo, "builderMethod", def.getBuilderMethod(), "build");
         doWriteAttribute(jo, "scriptLanguage", def.getScriptLanguage(), null);
         doWriteAttribute(jo, "scriptPropertyPlaceholders", 
def.getScriptPropertyPlaceholders(), "true");
-        doWriteChildElement(jo, "constructors", new 
BeanConstructorsAdapter().marshal(def.getConstructors()), 
this::doWriteBeanConstructorsDefinition);
-        doWriteChildElement(jo, "properties", new 
BeanPropertiesAdapter().marshal(def.getProperties()), 
this::doWriteBeanPropertiesDefinition);
+        doWriteBeanMap(jo, "constructors", def.getConstructors());
+        doWriteBeanMap(jo, "properties", def.getProperties());
         if (def.getScript() != null) {
             jo.put("script", def.getScript());
         }
@@ -1667,8 +1667,8 @@ public class YamlModelWriter extends 
YamlModelWriterSupport {
     protected JsonObject 
doWriteRouteTemplateDefinition(RouteTemplateDefinition def) {
         JsonObject jo = new JsonObject();
         doWriteOptionalIdentifiedDefinitionAttributes(jo, def);
-        doWriteChildList(jo, null, "templateParameter", 
def.getTemplateParameters(), this::doWriteRouteTemplateParameterDefinition);
-        doWriteChildList(jo, null, "templateBean", def.getTemplateBeans(), 
this::doWriteBeanFactoryDefinition);
+        doWriteChildList(jo, null, "parameters", def.getTemplateParameters(), 
this::doWriteRouteTemplateParameterDefinition);
+        doWriteChildList(jo, null, "beans", def.getTemplateBeans(), 
this::doWriteBeanFactoryDefinition);
         doWriteChildElement(jo, "route", def.getRoute(), 
this::doWriteRouteDefinition);
         return jo;
     }
@@ -2061,7 +2061,7 @@ public class YamlModelWriter extends 
YamlModelWriterSupport {
     }
     protected JsonObject doWriteValueDefinition(ValueDefinition def) {
         JsonObject jo = new JsonObject();
-        doWriteValue(jo, def.getValue());
+        doWriteAttribute(jo, "value", def.getValue(), null);
         return jo;
     }
     protected JsonObject doWriteWhenDefinition(WhenDefinition def) {
@@ -3411,7 +3411,7 @@ public class YamlModelWriter extends 
YamlModelWriterSupport {
         doWriteAttribute(jo, "enableNoContentResponse", 
def.getEnableNoContentResponse(), "false");
         doWriteAttribute(jo, "apiDocs", def.getApiDocs(), "true");
         doWriteAttribute(jo, "tag", def.getTag(), null);
-        doWriteChildList(jo, "verbs", "verbs", def.getVerbs(), 
this::doWriteVerbDefinition);
+        doWriteVerbs(jo, def.getVerbs(), this::doWriteVerbDefinition);
         doWriteChildElement(jo, "openApi", def.getOpenApi(), 
this::doWriteOpenApiDefinition);
         doWriteChildElement(jo, "securityDefinitions", 
def.getSecurityDefinitions(), this::doWriteRestSecuritiesDefinition);
         doWriteChildList(jo, null, "securityRequirements", 
def.getSecurityRequirements(), this::doWriteSecurityDefinition);
@@ -3480,9 +3480,9 @@ public class YamlModelWriter extends 
YamlModelWriterSupport {
         doWriteAttribute(jo, "consumes", def.getConsumes(), null);
     }
     protected void doWriteVerbDefinitionElements(JsonObject jo, VerbDefinition 
def) {
-        doWriteChildList(jo, "params", "params", def.getParams(), 
this::doWriteParamDefinition);
+        doWriteChildList(jo, null, "param", def.getParams(), 
this::doWriteParamDefinition);
         doWriteChildList(jo, "security", "security", def.getSecurity(), 
this::doWriteSecurityDefinition);
-        doWriteChildList(jo, "responseMsgs", "responseMsgs", 
def.getResponseMsgs(), this::doWriteResponseMessageDefinition);
+        doWriteChildList(jo, null, "responseMessage", def.getResponseMsgs(), 
this::doWriteResponseMessageDefinition);
         doWriteChildElement(jo, "to", def.getTo(), this::doWriteToDefinition);
     }
     protected JsonObject doWriteVerbDefinition(VerbDefinition def) {
diff --git 
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
 
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
index c5db9efafe79..22510f4548b7 100644
--- 
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
+++ 
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
@@ -24,7 +24,6 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
 import org.apache.camel.CamelContext;
@@ -407,63 +406,25 @@ public class LwModelToYAMLDumper implements 
ModelToYAMLDumper {
             if (beans.isEmpty()) {
                 return;
             }
-            buffer.write("- beans:\n");
+            // written by the model writer, so nested properties come out as 
nested maps, as the yaml dsl loads them
+            var writer = new YamlModelWriter() {
+                @Override
+                public JsonObject 
doWriteBeanFactoryDefinition(BeanFactoryDefinition<?> def) {
+                    return super.doWriteBeanFactoryDefinition(def);
+                }
+            };
+            writer.setCamelContext(camelContext);
+            JsonArray list = new JsonArray();
             for (BeanFactoryDefinition<?> b : beans) {
-                doWriteBeanFactoryDefinition(b);
-            }
-        }
-
-        private void doWriteBeanFactoryDefinition(BeanFactoryDefinition<?> b) {
-            String type = b.getType();
-            if (type.startsWith("#class:")) {
-                type = type.substring(7);
-            }
-            buffer.write(String.format("    - name: %s%n", b.getName()));
-            buffer.write(String.format("      type: \"%s\"%n", type));
-            if (b.getFactoryBean() != null) {
-                buffer.write(String.format("      factoryBean: \"%s\"%n", 
b.getFactoryBean()));
-            }
-            if (b.getFactoryMethod() != null) {
-                buffer.write(String.format("      factoryMethod: \"%s\"%n", 
b.getFactoryMethod()));
-            }
-            if (b.getBuilderClass() != null) {
-                buffer.write(String.format("      builderClass: \"%s\"%n", 
b.getBuilderClass()));
-            }
-            if (b.getBuilderMethod() != null) {
-                buffer.write(String.format("      builderMethod: \"%s\"%n", 
b.getBuilderMethod()));
-            }
-            if (b.getInitMethod() != null) {
-                buffer.write(String.format("      initMethod: \"%s\"%n", 
b.getInitMethod()));
-            }
-            if (b.getDestroyMethod() != null) {
-                buffer.write(String.format("      destroyMethod: \"%s\"%n", 
b.getDestroyMethod()));
-            }
-            if (b.getScriptLanguage() != null) {
-                buffer.write(String.format("      scriptLanguage: \"%s\"%n", 
b.getScriptLanguage()));
-            }
-            if (b.getScript() != null) {
-                buffer.write(String.format("      script: \"%s\"%n", 
b.getScript()));
-            }
-            if (b.getConstructors() != null && !b.getConstructors().isEmpty()) 
{
-                buffer.write(String.format("      constructors:%n"));
-                final AtomicInteger counter = new AtomicInteger();
-                b.getConstructors().forEach((key, value) -> {
-                    if (key == null) {
-                        key = counter.getAndIncrement();
-                    }
-                    buffer.write(String.format("        %d: \"%s\"%n", key, 
value));
-                });
-            }
-            if (b.getProperties() != null && !b.getProperties().isEmpty()) {
-                buffer.write(String.format("      properties:%n"));
-                b.getProperties().forEach((key, value) -> {
-                    if (value instanceof String) {
-                        buffer.write(String.format("        %s: \"%s\"%n", 
key, value));
-                    } else {
-                        buffer.write(String.format("        %s: %s%n", key, 
value));
-                    }
-                });
+                JsonObject bean = writer.doWriteBeanFactoryDefinition(b);
+                if (bean.get("type") instanceof String type && 
type.startsWith("#class:")) {
+                    bean.put("type", type.substring(7));
+                }
+                list.add(bean);
             }
+            JsonObject root = new JsonObject();
+            root.put("beans", list);
+            buffer.write(writer.printAsYaml(List.of(root)));
         }
     }
 
diff --git 
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/out/YamlModelWriterSupport.java
 
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/out/YamlModelWriterSupport.java
index 4fe49a127205..6af0454bb719 100644
--- 
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/out/YamlModelWriterSupport.java
+++ 
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/out/YamlModelWriterSupport.java
@@ -25,6 +25,7 @@ import java.util.function.Function;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.catalog.RuntimeCamelCatalog;
+import org.apache.camel.model.rest.VerbDefinition;
 import org.apache.camel.util.URISupport;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
@@ -143,6 +144,37 @@ public abstract class YamlModelWriterSupport {
         }
     }
 
+    /**
+     * The verbs of a rest as the YAML DSL has them: a list per kind (get, 
post, ...), not one list of all verbs.
+     */
+    protected <T extends VerbDefinition> void doWriteVerbs(JsonObject jo, 
List<T> verbs, Function<T, JsonObject> writer) {
+        if (verbs != null) {
+            for (T verb : verbs) {
+                JsonObject child = writer.apply(verb);
+                if (child != null) {
+                    JsonArray arr = (JsonArray) 
jo.computeIfAbsent(verb.asVerb(), k -> new JsonArray());
+                    arr.add(child);
+                }
+            }
+        }
+    }
+
+    /**
+     * The properties or constructor arguments of a bean as the YAML DSL has 
them: a map, with a nested map for nested
+     * properties.
+     */
+    protected void doWriteBeanMap(JsonObject jo, String key, Map<?, ?> map) {
+        if (map != null && !map.isEmpty()) {
+            jo.put(key, beanMap(map));
+        }
+    }
+
+    private static JsonObject beanMap(Map<?, ?> map) {
+        JsonObject answer = new JsonObject();
+        map.forEach((k, v) -> answer.put(String.valueOf(k), v instanceof 
Map<?, ?> m ? beanMap(m) : v));
+        return answer;
+    }
+
     @SuppressWarnings("unchecked")
     protected void doWriteStringList(JsonObject jo, String wrapperKey, String 
itemKey, List<String> list) {
         if (list != null && !list.isEmpty()) {
diff --git 
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlBeansDumpTest.java
 
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlBeansDumpTest.java
new file mode 100644
index 000000000000..28e23450a3e4
--- /dev/null
+++ 
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlBeansDumpTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.yaml.out;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.model.BeanFactoryDefinition;
+import org.apache.camel.model.app.BeansDefinition;
+import org.apache.camel.xml.in.ModelParser;
+import org.apache.camel.yaml.LwModelToYAMLDumper;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * The beans dumped as YAML have their properties as the YAML DSL loads them: 
nested properties as a nested map
+ * (CAMEL-25255).
+ */
+class YamlBeansDumpTest {
+
+    @Test
+    void nestedPropertiesAreANestedMap() throws Exception {
+        BeansDefinition beans;
+        try (InputStream in = 
Files.newInputStream(Paths.get("../camel-xml-io/src/test/resources/beansWithProperties.xml")))
 {
+            beans = new ModelParser(in, 
"http://camel.apache.org/schema/xml-io";).parseBeansDefinition().orElseThrow();
+        }
+        String yaml = new LwModelToYAMLDumper().dumpBeansAsYaml(null, new 
ArrayList<>(beans.getBeans()));
+        assertThat(yaml).isEqualTo("""
+                - beans:
+                    - name: b1
+                      type: org.apache.camel.xml.in.ModelParserTest.MyBean
+                      properties:
+                        p1: v1
+                        p2: v2
+                        nested:
+                          p1: v1a
+                          p2: v2a
+                    - name: b2
+                      type: org.apache.camel.xml.in.ModelParserTest.MyBean
+                      properties:
+                        p1: v1
+                        p2: v2
+                        nested.p1: v1a
+                        nested.p2: v2a
+                """);
+    }
+
+    @Test
+    void theClassPrefixOfTheTypeIsLeftOut() throws Exception {
+        BeanFactoryDefinition<?> bean = new BeanFactoryDefinition<>();
+        bean.setName("greeter");
+        bean.setType("#class:com.foo.Greeter");
+        String yaml = new LwModelToYAMLDumper().dumpBeansAsYaml(null, 
List.of(bean));
+        assertThat(yaml).contains("type: 
com.foo.Greeter").doesNotContain("#class");
+    }
+}
diff --git a/core/camel-yaml-io/src/test/resources/yaml-beans-with-route.yaml 
b/core/camel-yaml-io/src/test/resources/yaml-beans-with-route.yaml
index 64db76b553c7..02359c8af424 100644
--- a/core/camel-yaml-io/src/test/resources/yaml-beans-with-route.yaml
+++ b/core/camel-yaml-io/src/test/resources/yaml-beans-with-route.yaml
@@ -20,9 +20,7 @@
       - name: myService
         type: com.example.MyService
         properties:
-          property:
-            - key: url
-              value: http://localhost:8080
+          url: http://localhost:8080
     route:
       - customId: true
         id: myRoute
diff --git a/core/camel-yaml-io/src/test/resources/yaml-beans.yaml 
b/core/camel-yaml-io/src/test/resources/yaml-beans.yaml
index 3e50198f4fdc..841c9322fc89 100644
--- a/core/camel-yaml-io/src/test/resources/yaml-beans.yaml
+++ b/core/camel-yaml-io/src/test/resources/yaml-beans.yaml
@@ -19,11 +19,8 @@
     name: myBean
     type: com.example.MyBean
     properties:
-      property:
-        - key: greeting
-          value: Hello
-        - key: count
-          value: 5
+      greeting: Hello
+      count: "5"
 - beanFactory:
     name: myFactory
     type: com.example.MyFactory
diff --git a/core/camel-yaml-io/src/test/resources/yaml-rest.yaml 
b/core/camel-yaml-io/src/test/resources/yaml-rest.yaml
index fee9da12aaaa..9ee93c87a537 100644
--- a/core/camel-yaml-io/src/test/resources/yaml-rest.yaml
+++ b/core/camel-yaml-io/src/test/resources/yaml-rest.yaml
@@ -17,10 +17,11 @@
 
 - rest:
     path: /api
-    verbs:
+    get:
       - path: /hello
         to:
           uri: direct:hello
+    post:
       - path: /bye
         consumes: application/json
         to:
diff --git a/core/camel-yaml-io/src/test/resources/yaml-route-template.yaml 
b/core/camel-yaml-io/src/test/resources/yaml-route-template.yaml
index a1ad10bd8c38..544d3a05f61a 100644
--- a/core/camel-yaml-io/src/test/resources/yaml-route-template.yaml
+++ b/core/camel-yaml-io/src/test/resources/yaml-route-template.yaml
@@ -18,7 +18,7 @@
 - routeTemplate:
     customId: true
     id: myTemplate
-    templateParameter:
+    parameters:
       - description: the foo parameter
         name: foo
       - description: the bar parameter
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/blueprint-out.yaml 
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/blueprint-out.yaml
index 754bcf2ae5e9..e304ac8d51cc 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/test/resources/blueprint-out.yaml
+++ b/dsl/camel-jbang/camel-jbang-core/src/test/resources/blueprint-out.yaml
@@ -17,7 +17,7 @@
 
 - beans:
     - name: userService
-      type: "org.apache.camel.example.rest.UserService"
+      type: org.apache.camel.example.rest.UserService
 - route:
     from:
       uri: direct
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlRestTemplateRoundTripTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlRestTemplateRoundTripTest.java
new file mode 100644
index 000000000000..e6e5792cf56e
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlRestTemplateRoundTripTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.validator;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import com.networknt.schema.Error;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.RouteTemplatesDefinition;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.xml.LwModelToXMLDumper;
+import org.apache.camel.xml.in.ModelParser;
+import org.apache.camel.yaml.LwModelToYAMLDumper;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+/**
+ * Rests and route templates dumped as YAML are loaded back by the YAML DSL as 
they were (CAMEL-25255): the YAML writer
+ * writes the verbs of a rest by their kind (get, post, ...), and the 
parameters and beans of a route template as the
+ * YAML DSL names them.
+ */
+class YamlRestTemplateRoundTripTest {
+
+    private static final String NS = "http://camel.apache.org/schema/xml-io";;
+    private static final Path CORPUS = 
Path.of("../../../core/camel-xml-io/src/test/resources");
+
+    private static final String REST = """
+            <rests xmlns="http://camel.apache.org/schema/xml-io";>
+                <rest id="api" path="/api" tag="orders" 
consumes="application/json" bindingMode="json">
+                    <securityDefinitions>
+                        <apiKey key="key" name="X-Key" inHeader="true"/>
+                        <oauth2 key="oauth" flow="implicit" 
authorizationUrl="https://auth/authorize";>
+                            <scopes key="read" value="Read orders"/>
+                        </oauth2>
+                    </securityDefinitions>
+                    <securityRequirements key="key"/>
+                    <get path="/orders/{id}" id="getOrder" 
outType="com.foo.Order" routeId="get-order"
+                         description="Gets an order">
+                        <param name="id" type="path" description="The order 
id" dataType="integer">
+                            <examples key="one" value="1"/>
+                        </param>
+                        <param name="verbose" type="query" required="false" 
defaultValue="false">
+                            <allowableValues>
+                                <value>true</value>
+                                <value>false</value>
+                            </allowableValues>
+                        </param>
+                        <responseMessage code="404" message="Not found">
+                            <header name="X-Reason" dataType="string"/>
+                            <examples key="json" value="{}"/>
+                        </responseMessage>
+                        <security key="oauth" scopes="read"/>
+                        <to uri="direct:getOrder"/>
+                    </get>
+                    <get path="/orders">
+                        <to uri="direct:listOrders"/>
+                    </get>
+                    <post path="/orders" type="com.foo.Order">
+                        <to uri="direct:createOrder"/>
+                    </post>
+                    <put path="/orders/{id}">
+                        <to uri="direct:updateOrder"/>
+                    </put>
+                    <delete path="/orders/{id}">
+                        <to uri="direct:deleteOrder"/>
+                    </delete>
+                    <head path="/orders">
+                        <to uri="direct:headOrders"/>
+                    </head>
+                    <patch path="/orders/{id}">
+                        <to uri="direct:patchOrder"/>
+                    </patch>
+                </rest>
+                <rest path="/petstore">
+                    <openApi specification="petstore.json" 
missingOperation="mock"/>
+                </rest>
+            </rests>
+            """;
+
+    private static final String TEMPLATE = """
+            <routeTemplates xmlns="http://camel.apache.org/schema/xml-io";>
+                <routeTemplate id="myTemplate">
+                    <templateParameter name="foo" description="The input"/>
+                    <templateParameter name="bar" defaultValue="out" 
required="false"/>
+                    <templateBean name="greeter" type="com.foo.Greeter">
+                        <properties>
+                            <property key="greeting" value="Hello"/>
+                        </properties>
+                    </templateBean>
+                    <route>
+                        <from uri="direct:{{foo}}"/>
+                        <to uri="mock:{{bar}}"/>
+                    </route>
+                </routeTemplate>
+            </routeTemplates>
+            """;
+
+    @Test
+    void restWithAllItsParts() throws Exception {
+        assertThat(restsRoundTrip(REST)).isNull();
+    }
+
+    @Test
+    void routeTemplateWithParametersAndBeans() throws Exception {
+        assertThat(templatesRoundTrip(TEMPLATE)).isNull();
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "barRest.xml", "simpleRest.xml", 
"restAllowedValues.xml" })
+    void restsOfTheCorpus(String file) throws Exception {
+        assumeTrue(Files.isDirectory(CORPUS), "the core test routes are in the 
source tree");
+        
assertThat(restsRoundTrip(Files.readString(CORPUS.resolve(file)))).isNull();
+    }
+
+    @Test
+    void routeTemplatesOfTheCorpus() throws Exception {
+        assumeTrue(Files.isDirectory(CORPUS), "the core test routes are in the 
source tree");
+        
assertThat(templatesRoundTrip(Files.readString(CORPUS.resolve("barTemplate.xml")))).isNull();
+    }
+
+    private static InputStream in(String xml) {
+        return new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * Null when the rests read back as they were, else what went wrong. Both 
sides are added to a context the same way,
+     * so what loading adds (such as the params of a path) is on both.
+     */
+    private static String restsRoundTrip(String xml) throws Exception {
+        RestsDefinition rests = new ModelParser(in(xml), 
NS).parseRestsDefinition().orElseThrow();
+        String yaml;
+        String before;
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            yaml = new LwModelToYAMLDumper().dumpModelAsYaml(context, rests);
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    getRestCollection().setRests(rests.getRests());
+                }
+            });
+            before = dumpRests(context);
+        }
+        return readBack(yaml, before, 
YamlRestTemplateRoundTripTest::dumpRests);
+    }
+
+    /** Null when the route templates read back as they were, else what went 
wrong. */
+    private static String templatesRoundTrip(String xml) throws Exception {
+        RouteTemplatesDefinition templates = new ModelParser(in(xml), 
NS).parseRouteTemplatesDefinition().orElseThrow();
+        String yaml;
+        String before;
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            yaml = new LwModelToYAMLDumper().dumpModelAsYaml(context, 
templates);
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    
getRouteTemplateCollection().setRouteTemplates(templates.getRouteTemplates());
+                }
+            });
+            before = dumpTemplates(context);
+        }
+        return readBack(yaml, before, 
YamlRestTemplateRoundTripTest::dumpTemplates);
+    }
+
+    private static String dumpRests(DefaultCamelContext context) throws 
Exception {
+        RestsDefinition rests = new RestsDefinition();
+        
rests.setRests(context.getCamelContextExtension().getContextPlugin(ModelCamelContext.class).getRestDefinitions());
+        return new LwModelToXMLDumper().dumpModelAsXml(context, rests);
+    }
+
+    private static String dumpTemplates(DefaultCamelContext context) throws 
Exception {
+        RouteTemplatesDefinition templates = new RouteTemplatesDefinition();
+        
templates.setRouteTemplates(context.getCamelContextExtension().getContextPlugin(ModelCamelContext.class)
+                .getRouteTemplateDefinitions());
+        return new LwModelToXMLDumper().dumpModelAsXml(context, templates);
+    }
+
+    private interface Dump {
+        String dump(DefaultCamelContext context) throws Exception;
+    }
+
+    private static String readBack(String yaml, String before, Dump dump) 
throws Exception {
+        List<Error> errors = new YamlValidator().validate(yaml);
+        if (!errors.isEmpty()) {
+            return yaml + "\n--- does not validate: " + errors;
+        }
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            
PluginHelper.getRoutesLoader(context).loadRoutes(ResourceHelper.fromString("rest.yaml",
 yaml));
+            String after = dump.dump(context);
+            return after.equals(before) ? null : yaml + "\n--- was ---\n" + 
before + "\n--- read back as ---\n" + after;
+        } catch (RuntimeException e) {
+            return yaml + "\n--- does not load: " + e;
+        }
+    }
+}
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
 
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
index 711796bc892a..c9153ee6f1a6 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/model-yaml-writer.vm
@@ -15,6 +15,8 @@
 ## limitations under the License.
 ## ---------------------------------------------------------------------------
 #set( $default = "#[[##default]]#" )
+## the YAML DSL names some lists other than the XML DSL does (keyed by 
class.member)
+#set( $yamlKeys = { "VerbDefinition.params": "param", 
"VerbDefinition.responseMsgs": "responseMessage", 
"RouteTemplateDefinition.templateParameters": "parameters", 
"RouteTemplateDefinition.templateBeans": "beans" } )
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -141,8 +143,13 @@ public class YamlModelWriter extends 
YamlModelWriterSupport {
                     #elseif( $member.xmlElementRef )
                         #set( $foo = $elementRefs.add($root) )
                         #if( $list )
+                            #set( $yamlKey = 
$yamlKeys.get("${clazz.simpleName}.${member.name}") )
                             #if( $member.name == "outputs" )
         doWriteOutputs(jo, def.${member.getter}(), 
this::doWrite${root.simpleName}Ref);
+                            #elseif( $clazz.simpleName == "RestDefinition" && 
$member.name == "verbs" )
+        doWriteVerbs(jo, def.${member.getter}(), 
this::doWrite${root.simpleName});
+                            #elseif( $yamlKey )
+        doWriteChildList(jo, null, "${yamlKey}", def.${member.getter}(), 
this::doWrite${root.simpleName});
                             #else
                                 #set( $refName = $member.xmlElementRef.name() )
                                 #if( $refName && $refName != $default && 
$refName != "" )
@@ -186,6 +193,10 @@ public class YamlModelWriter extends 
YamlModelWriterSupport {
                         #if( $n == $default )
                             #set( $n = ${member.name} )
                         #end
+                        #set( $yamlKey = 
$yamlKeys.get("${clazz.simpleName}.${member.name}") )
+                        #if( $yamlKey )
+                            #set( $n = $yamlKey )
+                        #end
                         #if( $list )
                             #set( $w = false )
                             #if( $member.xmlElementWrapper )
@@ -198,7 +209,9 @@ public class YamlModelWriter extends YamlModelWriterSupport 
{
                             #end
                         #else
                             #set( $adapter = $member.xmlJavaTypeAdapter )
-                            #if( $adapter )
+                            #if( $adapter && $clazz.simpleName == 
"BeanFactoryDefinition" )
+        doWriteBeanMap(jo, "${n}", def.${member.getter}());
+                            #elseif( $adapter )
                                 #set( $cl = $adapter.value() )
                                 #set( $actualType = false )
                                 #foreach( $m in $cl.declaredMethods )
@@ -268,7 +281,9 @@ public class YamlModelWriter extends YamlModelWriterSupport 
{
                 #writeYamlAttrs
             #end
             #set( $value = 
$mojo.getValues($mojo.getClassAndSuper($clazz)).findFirst() )
-            #if( $value.isPresent() )
+            #if( $value.isPresent() && $name == "ValueDefinition" )
+        doWriteAttribute(jo, "value", def.${value.get().getter}(), null);
+            #elseif( $value.isPresent() )
         doWriteValue(jo, def.${value.get().getter}());
             #end
             #if( $hasDerived && !$thisElements.isEmpty() )

Reply via email to