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 b69d769784d6 CAMEL-24723: camel-jbang - camel validate yaml 
--canonical reports the deprecated compact notation as such; the runtime WARN 
covers implicit expressions (#26403)
b69d769784d6 is described below

commit b69d769784d69592aa0ebe7a6e8c2673ac543a96
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 14 19:46:02 2026 +0200

    CAMEL-24723: camel-jbang - camel validate yaml --canonical reports the 
deprecated compact notation as such; the runtime WARN covers implicit 
expressions (#26403)
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
 .../org/apache/camel/catalog/docs/yaml-dsl.adoc    |  11 +-
 .../commands/validate/YamlValidateCommand.java     |   3 +-
 .../dsl/yaml/common/YamlDeserializerBase.java      |  16 ++-
 .../dsl/yaml/deserializers/ModelDeserializers.java |  52 ++++++++
 .../dsl/yaml/GenerateYamlDeserializersMojo.java    |   2 +
 .../camel/dsl/yaml/validator/YamlValidator.java    |  87 +++++++++++++
 .../yaml/validator/YamlCanonicalValidatorTest.java |  70 +++++++++++
 .../camel-yaml-dsl/src/main/docs/yaml-dsl.adoc     |  11 +-
 .../camel/dsl/yaml/CompactNotationWarnTest.groovy  | 136 +++++++++++++++++++++
 9 files changed, 379 insertions(+), 9 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
index c403e8b2d8cf..3b172e67cd44 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
@@ -883,7 +883,9 @@ camel validate normalize --output normalized/ myroute.yaml
 === Compact notation warning
 
 Camel logs a WARN message when YAML routes use compact (shorthand) notation 
instead of
-the canonical (explicit) form. This is to encourage adopting the canonical 
style which is
+the canonical (explicit) form: a step written as a string (`log: "..."`), a 
language written as a
+string (`simple: "..."`), or a language key directly on the EIP (`setBody: 
{simple: ...}`) instead of
+under `expression:`. This is to encourage adopting the canonical style which is
 more friendly for tooling and AI assistants.
 
 The warning is logged once per resource file and looks like:
@@ -910,7 +912,12 @@ To validate YAML routes against the canonical schema:
 camel validate yaml --canonical myroute.yaml
 ----
 
-This reports any shorthands or implicit forms that are not valid in the 
canonical schema.
+This reports each use of the compact notation with the canonical form to 
write, for example:
+
+----
+/0/route/from/steps/0/setBody: setBody: {simple: ...} is the deprecated 
compact notation: an expression is written under expression: (setBody: 
{expression: {simple: {expression: "..."}}}); camel validate normalize rewrites 
a file in the canonical format
+/0/route/from/steps/1/log: log: "..." is the deprecated compact notation: 
write log: {message: "..."}; camel validate normalize rewrites a file in the 
canonical format
+----
 
 The `YamlValidator` class supports both schemas programmatically:
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
 
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
index 0070868ac4b7..e0591e317f7a 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
@@ -42,7 +42,8 @@ public class YamlValidateCommand extends CamelCommand {
     private static final String IGNORE_FILE = "application";
 
     @CommandLine.Option(names = { "--canonical" }, defaultValue = "false",
-                        description = "Validate against the canonical schema 
(rejects shorthands and implicit expressions)")
+                        description = "Validate against the canonical schema: 
reports the deprecated compact notation (string shorthands,"
+                                      + " implicit expressions) with the 
canonical form to write")
     boolean canonical;
 
     @CommandLine.Option(names = { "--catalog" }, defaultValue = "true",
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerBase.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerBase.java
index b078257335b8..a250e55c67d5 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerBase.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerBase.java
@@ -56,10 +56,7 @@ public abstract class YamlDeserializerBase<T> extends 
YamlDeserializerSupport im
         if (node.getNodeType() == NodeType.SCALAR) {
             ScalarNode mn = (ScalarNode) node;
             target = newInstance(mn.getValue());
-            YamlDeserializationContext ctx = getDeserializationContext(node);
-            if (ctx != null) {
-                ctx.warnCompactNotationOnce(LOG);
-            }
+            warnCompactNotation(node);
             // line number points to the scalar itself, so it should be +1
             if (line != -1) {
                 line++;
@@ -92,6 +89,17 @@ public abstract class YamlDeserializerBase<T> extends 
YamlDeserializerSupport im
         // noop
     }
 
+    /**
+     * The compact notation (a step or a language written as a string, a 
language key directly on the EIP) is
+     * deprecated: warns once per resource that the canonical notation is 
recommended.
+     */
+    protected static void warnCompactNotation(Node node) {
+        YamlDeserializationContext ctx = getDeserializationContext(node);
+        if (ctx != null) {
+            ctx.warnCompactNotationOnce(LOG);
+        }
+    }
+
     /**
      * Creates a Java instance of the expected type from a string.
      *
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 14df4efb7e87..416175fdc142 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
@@ -1864,6 +1864,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -2297,6 +2298,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -4204,6 +4206,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -4529,6 +4532,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -4860,6 +4864,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -5125,6 +5130,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -5271,6 +5277,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -5823,6 +5830,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -6423,6 +6431,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -6844,6 +6853,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -6922,6 +6932,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -7075,6 +7086,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -7651,6 +7663,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -7735,6 +7748,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -7807,6 +7821,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -8050,6 +8065,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -8128,6 +8144,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -8475,6 +8492,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -9083,6 +9101,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -9462,6 +9481,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -9932,6 +9952,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -10252,6 +10273,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -10517,6 +10539,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -10880,6 +10903,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -12276,6 +12300,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -12655,6 +12680,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -13068,6 +13094,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -13140,6 +13167,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -13212,6 +13240,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -13418,6 +13447,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -13724,6 +13754,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -14176,6 +14207,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -15876,6 +15908,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -16352,6 +16385,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -16470,6 +16504,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -16618,6 +16653,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -16760,6 +16796,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -16839,6 +16876,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -16992,6 +17030,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -17209,6 +17248,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -17281,6 +17321,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -17473,6 +17514,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -18482,6 +18524,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -18993,6 +19036,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -19342,6 +19386,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -20465,6 +20510,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -20646,6 +20692,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -20724,6 +20771,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -20859,6 +20907,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -21214,6 +21263,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -21352,6 +21402,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
@@ -21448,6 +21499,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node);
                     if (ed != null) {
                         target.setExpressionType(ed);
+                        warnCompactNotation(node);
                     } else {
                         return false;
                     }
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
index 30687e18fb70..8b81504b1883 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
@@ -627,6 +627,8 @@ public class GenerateYamlDeserializersMojo extends 
GenerateYamlSupportMojo {
             setProperty.addStatement("ed = 
ExpressionDeserializers.constructExpressionType(propertyKey, node)");
             setProperty.beginControlFlow("if (ed != null)");
             setProperty.addStatement("target.setExpressionType(ed)");
+            // the language key directly on the EIP is the compact notation 
(canonical is under expression:)
+            setProperty.addStatement("warnCompactNotation(node)");
             setProperty.nextControlFlow("else");
             setProperty.addStatement("return false");
             setProperty.endControlFlow();
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
index 2b7e191b064a..884b57cf4c8d 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
@@ -267,6 +267,9 @@ public class YamlValidator {
     private List<Error> validate(JsonNode target) {
         var errors = filterOneOfNoise(new 
ArrayList<>(schema.validate(target)));
         errors.removeIf(YamlValidator::isRuntimeAcceptedScalar);
+        if (canonical) {
+            errors = withCompactNotationHints(errors);
+        }
         errors = withExpressionHints(errors);
         errors = withPropertyHints(errors);
         errors = withListHints(errors);
@@ -303,6 +306,90 @@ public class YamlValidator {
         return errors;
     }
 
+    /** The property a step written as a string sets: the argument of the 
definition's String constructor. */
+    private static final Map<String, String> STRING_STEP_PROPERTY = 
Map.ofEntries(
+            Map.entry("bean", "ref"), Map.entry("convertBodyTo", "type"), 
Map.entry("log", "message"),
+            Map.entry("poll", "uri"), Map.entry("removeHeader", "name"), 
Map.entry("removeHeaders", "pattern"),
+            Map.entry("removeProperties", "pattern"), 
Map.entry("removeProperty", "name"),
+            Map.entry("removeVariable", "name"), Map.entry("rollback", 
"message"),
+            Map.entry("setExchangePattern", "pattern"), Map.entry("to", 
"uri"), Map.entry("toD", "uri"));
+
+    private static final String NORMALIZE_HINT = "; camel validate normalize 
rewrites a file in the canonical format";
+
+    /**
+     * The canonical schema rejects the compact notation as a schema error 
that says nothing about it: "property
+     * 'simple' is not defined" for a language key directly on the EIP, 
"string found, object expected" for a step or a
+     * language written as a string. Each is replaced with a message that 
names the notation, the canonical form of that
+     * line, and the normalize command.
+     */
+    List<Error> withCompactNotationHints(List<Error> errors) {
+        List<Error> answer = new ArrayList<>(errors.size());
+        for (Error error : errors) {
+            String hint = compactNotationHint(error);
+            if (hint == null) {
+                answer.add(error);
+                continue;
+            }
+            answer.add(Error.builder()
+                    .keyword("compactNotation")
+                    .instanceLocation(error.getInstanceLocation())
+                    .messageKey("compactNotation")
+                    .format(new MessageFormat("{0}"))
+                    .arguments(hint + NORMALIZE_HINT)
+                    .build());
+        }
+        return answer;
+    }
+
+    private String compactNotationHint(Error error) {
+        String message = error.getMessage();
+        if (message == null) {
+            return null;
+        }
+        String location = String.valueOf(error.getInstanceLocation());
+        String name = location.substring(location.lastIndexOf('/') + 1);
+        if ("additionalProperties".equals(error.getKeyword())) {
+            // setBody: {simple: ...} or when: [- simple: ...]: the language 
key sits on the EIP, not under expression:
+            String unknown = between(message, "property '", "'");
+            if (unknown == null || !languageKeys.contains(unknown)) {
+                return null;
+            }
+            if (name.matches("\\d+")) {
+                String parent = location.substring(0, 
location.lastIndexOf('/'));
+                name = parent.substring(parent.lastIndexOf('/') + 1);
+                return "a " + name + " item with " + unknown + ": ... is the 
deprecated compact notation: an expression"
+                       + " is written under expression: (- expression: {" + 
unknown + ": {" + languageForm(unknown)
+                       + "}})";
+            }
+            return name + ": {" + unknown + ": ...} is the deprecated compact 
notation: an expression is written under"
+                   + " expression: (" + name + ": {expression: {" + unknown + 
": {" + languageForm(unknown) + "}}})";
+        }
+        if ("type".equals(error.getKeyword()) && message.contains("string 
found, object expected")) {
+            if (languageKeys.contains(name)) {
+                // simple: "..." : the language is a map with its expression
+                return name + ": \"...\" is the deprecated compact notation: 
write " + name + ": {" + languageForm(name)
+                       + "}";
+            }
+            if (stepNames.contains(name) || topLevelEntries.contains(name)) {
+                // log: "..." : the step is a map with its properties
+                String property = STRING_STEP_PROPERTY.get(name);
+                return name + ": \"...\" is the deprecated compact notation: 
write " + name
+                       + (property != null ? ": {" + property + ": \"...\"}" : 
" as a map with its properties");
+            }
+        }
+        return null;
+    }
+
+    /** The canonical body of a language: its expression property, or token 
for tokenize, as key: "...". */
+    private String languageForm(String language) {
+        JsonNode ref = 
model.at("/items/definitions/org.apache.camel.model.language.ExpressionDefinition/properties/"
+                                + language + "/$ref");
+        JsonNode properties = ref.isTextual() ? 
model.at(ref.asText().substring(1) + "/properties") : null;
+        String property = properties != null && properties.has("expression") ? 
"expression"
+                : properties != null && properties.has("token") ? "token" : 
"expression";
+        return property + ": \"...\"";
+    }
+
     /**
      * "must have at most 1 properties" at a step: a step holds one EIP, and 
the second key is either an option that
      * belongs under the EIP (indented one level more) or another step (its 
own - item).
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
index 45c5c17de9e3..73dd235aea44 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
@@ -162,4 +162,74 @@ public class YamlCanonicalValidatorTest {
         var report = canonicalValidator.validate(route);
         assertThat(report).as("loadBalance with two load balancers should fail 
canonical validation").isNotEmpty();
     }
+
+    // CAMEL-24723: the compact notation is reported as such, one message per 
occurrence, with the canonical form
+    @Test
+    public void testCompactNotationIsReportedWithTheCanonicalForm() throws 
Exception {
+        var route = """
+                - route:
+                    from:
+                      uri: "direct:start"
+                      steps:
+                        - setBody:
+                            simple: "Hello ${body}"
+                        - setHeader:
+                            name: foo
+                            expression:
+                              constant: "bar"
+                        - log: "${body}"
+                        - choice:
+                            when:
+                              - simple: "${header.foo} == 'bar'"
+                                steps:
+                                  - to: "mock:a"
+                        - split:
+                            tokenize: ","
+                            steps:
+                              - log:
+                                  message: "x"
+                - onException:
+                    exception:
+                      - java.lang.Exception
+                    handled:
+                      constant: "true"
+                    steps:
+                      - log:
+                          message: "err"
+                """;
+        var report = canonicalValidator.validate(route);
+        assertThat(report).extracting(e -> e.getInstanceLocation() + ": " + 
e.getMessage()).containsExactly(
+                "/0/route/from/steps/0/setBody: setBody: {simple: ...} is the 
deprecated compact notation: an expression"
+                                                                               
                             + " is written under expression: (setBody: 
{expression: {simple: {expression: \"...\"}}});"
+                                                                               
                             + " camel validate normalize rewrites a file in 
the canonical format",
+                "/0/route/from/steps/1/setHeader/expression/constant: 
constant: \"...\" is the deprecated compact"
+                                                                               
                                                                                
                    + " notation: write constant: {expression: \"...\"};"
+                                                                               
                                                                                
                    + " camel validate normalize rewrites a file in the 
canonical format",
+                "/0/route/from/steps/2/log: log: \"...\" is the deprecated 
compact notation: write log: {message: \"...\"};"
+                                                                               
                                                                                
                                                                                
           + " camel validate normalize rewrites a file in the canonical 
format",
+                "/0/route/from/steps/3/choice/when/0/steps/0/to: to: \"...\" 
is the deprecated compact notation: write"
+                                                                               
                                                                                
                                                                                
                                                                                
  + " to: {uri: \"...\"}; camel validate normalize rewrites a file in the 
canonical format",
+                "/0/route/from/steps/3/choice/when/0: a when item with simple: 
... is the deprecated compact notation:"
+                                                                               
                                                                                
                                                                                
                                                                                
                                                                                
             + " an expression is written under expression: (- expression: 
{simple: {expressi [...]
+                                                                               
                                                                                
                                                                                
                                                                                
                                                                                
             + " camel validate normalize rewrites a file in the canonical 
format",
+                "/0/route/from/steps/4/split: split: {tokenize: ...} is the 
deprecated compact notation: an expression"
+                                                                               
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
    + " is wr [...]
+                                                                               
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
    + " camel [...]
+                "/1/onException/handled/constant: constant: \"...\" is the 
deprecated compact notation: write"
+                                                                               
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+    }
+
+    @Test
+    public void testCompactNotationIsNotReportedByTheClassicValidator() throws 
Exception {
+        var route = """
+                - route:
+                    from:
+                      uri: "direct:start"
+                      steps:
+                        - setBody:
+                            simple: "Hello ${body}"
+                        - log: "${body}"
+                """;
+        assertThat(classicValidator.validate(route)).isEmpty();
+    }
 }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
index c403e8b2d8cf..3b172e67cd44 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
@@ -883,7 +883,9 @@ camel validate normalize --output normalized/ myroute.yaml
 === Compact notation warning
 
 Camel logs a WARN message when YAML routes use compact (shorthand) notation 
instead of
-the canonical (explicit) form. This is to encourage adopting the canonical 
style which is
+the canonical (explicit) form: a step written as a string (`log: "..."`), a 
language written as a
+string (`simple: "..."`), or a language key directly on the EIP (`setBody: 
{simple: ...}`) instead of
+under `expression:`. This is to encourage adopting the canonical style which is
 more friendly for tooling and AI assistants.
 
 The warning is logged once per resource file and looks like:
@@ -910,7 +912,12 @@ To validate YAML routes against the canonical schema:
 camel validate yaml --canonical myroute.yaml
 ----
 
-This reports any shorthands or implicit forms that are not valid in the 
canonical schema.
+This reports each use of the compact notation with the canonical form to 
write, for example:
+
+----
+/0/route/from/steps/0/setBody: setBody: {simple: ...} is the deprecated 
compact notation: an expression is written under expression: (setBody: 
{expression: {simple: {expression: "..."}}}); camel validate normalize rewrites 
a file in the canonical format
+/0/route/from/steps/1/log: log: "..." is the deprecated compact notation: 
write log: {message: "..."}; camel validate normalize rewrites a file in the 
canonical format
+----
 
 The `YamlValidator` class supports both schemas programmatically:
 
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CompactNotationWarnTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CompactNotationWarnTest.groovy
new file mode 100644
index 000000000000..a5a52082d4c0
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CompactNotationWarnTest.groovy
@@ -0,0 +1,136 @@
+/*
+ * 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.dsl.yaml.common.YamlDeserializerBase
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.LogManager
+import org.apache.logging.log4j.core.LogEvent
+import org.apache.logging.log4j.core.Logger
+import org.apache.logging.log4j.core.appender.AbstractAppender
+import org.apache.logging.log4j.core.config.Property
+
+/**
+ * CAMEL-24723: the compact notation is deprecated and the deserializers warn 
once per resource when they meet it,
+ * for a language key directly on the EIP as much as for a step or a language 
written as a string.
+ */
+class CompactNotationWarnTest extends YamlTestSupport {
+
+    List<String> warnings = []
+    AbstractAppender appender
+
+    def setup() {
+        appender = new AbstractAppender("compact-notation", null, null, false, 
Property.EMPTY_ARRAY) {
+            @Override
+            void append(LogEvent event) {
+                if (event.level == Level.WARN) {
+                    warnings << event.message.formattedMessage
+                }
+            }
+        }
+        appender.start()
+        ((Logger) 
LogManager.getLogger(YamlDeserializerBase)).addAppender(appender)
+    }
+
+    def cleanup() {
+        ((Logger) 
LogManager.getLogger(YamlDeserializerBase)).removeAppender(appender)
+        appender.stop()
+    }
+
+    def "a language key directly on the EIP is the compact notation"() {
+        when:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: "direct:start"
+                      steps:
+                        - setBody:
+                            simple:
+                              expression: "Hello ${body}"
+                        - to:
+                            uri: "mock:result"
+            '''
+        then:
+            warnings.size() == 1
+            warnings[0].contains('YAML DSL compact notation detected')
+            warnings[0].contains('camel validate normalize')
+    }
+
+    def "a step written as a string is the compact notation"() {
+        when:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: "direct:start"
+                      steps:
+                        - log: "${body}"
+            '''
+        then:
+            warnings.size() == 1
+    }
+
+    def "a language written as a string is the compact notation"() {
+        when:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: "direct:start"
+                      steps:
+                        - setBody:
+                            expression:
+                              constant: "Hello"
+            '''
+        then:
+            warnings.size() == 1
+    }
+
+    def "the canonical notation is not warned about"() {
+        when:
+            loadRoutes '''
+                - onException:
+                    exception:
+                      - java.lang.Exception
+                    handled:
+                      constant:
+                        expression: "true"
+                    steps:
+                      - log:
+                          message: "err"
+                - route:
+                    from:
+                      uri: "direct:start"
+                      steps:
+                        - setBody:
+                            expression:
+                              simple:
+                                expression: "Hello ${body}"
+                        - choice:
+                            when:
+                              - expression:
+                                  simple:
+                                    expression: "${body} == 'x'"
+                                steps:
+                                  - log:
+                                      message: "x"
+                        - to:
+                            uri: "mock:result"
+            '''
+        then:
+            warnings.isEmpty()
+    }
+}

Reply via email to