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

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

commit 40105f23e006573397a94af2b36e361424024933
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Aug 27 18:52:43 2026 +0200

    CAMEL-24482: camel-yaml-dsl - Canonical schema wrongly requires every data 
format on unmarshal
    
    The canonical YAML DSL JSON schema disables the oneOf grouping used to
    express "pick exactly one of N alternatives" (GenerateYamlSchemaMojo's
    isInOneOf is always false in canonical mode). Since each unmarshal data
    format alternative also carries required=true (needed to enforce the
    "one of N" cardinality in the non-canonical, oneOf-based schema), that
    required flag leaked straight into the flat top-level "required" list,
    turning "choose one of 50 data formats" into "all 50 are mandatory".
    
    marshal.dataFormatType lacked the same required=true annotation, so it
    accidentally avoided the bug while also silently accepting a
    marshal step with no data format at all (a route that fails at build
    time with "type must be specified"). Align it with unmarshal so both
    enforce the same "exactly one data format" contract.
    
    Since a flat, oneOf-free canonical schema cannot itself express "exactly
    one of N" cardinality, YamlValidator now re-checks it directly, driven
    by the same "oneOf"/"required" metadata already published in the Camel
    catalog's EIP model JSON - covering not just data formats, but every
    "pick one" option group (expression languages, error handlers, load
    balancers, resequencer config, tokenizers) without hardcoding any of
    their alternative names.
    
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel/catalog/schemas/camelYamlDsl-model.json  | 158 +++++++--------------
 .../org/apache/camel/model/MarshalDefinition.java  |   3 +-
 .../dsl/yaml/deserializers/ModelDeserializers.java | 100 ++++++-------
 .../maven/dsl/yaml/GenerateYamlSchemaMojo.java     |   7 +-
 .../camel-yaml-dsl-validator/pom.xml               |   4 +
 .../camel/dsl/yaml/validator/YamlValidator.java    | 123 +++++++++++++++-
 .../yaml/validator/YamlCanonicalValidatorTest.java |  66 +++++++++
 .../canonical-invalid-missing-dataformat.yaml      |  22 +++
 .../test/resources/canonical-valid-dataformat.yaml |  27 ++++
 .../resources/schema/camelYamlDsl-canonical.json   |   8 +-
 .../resources/schema/camelYamlDsl-model.json       | 158 +++++++--------------
 .../generated/resources/schema/camelYamlDsl.json   | 104 --------------
 .../org/apache/camel/dsl/yaml/MarshalTest.groovy   |  12 +-
 13 files changed, 417 insertions(+), 375 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
index 80f73eaa4b1e..9a683bb8bf48 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
@@ -5301,11 +5301,11 @@
         "ref" : "expression",
         "description" : "The property value as an expression.",
         "title" : "Expression",
-        "required" : true,
         "displayName" : "Expression",
         "kind" : "expression",
         "index" : 1,
-        "group" : "common"
+        "group" : "common",
+        "required" : true
       } ]
     },
     "recipientList" : {
@@ -6154,11 +6154,11 @@
         "ref" : "expression",
         "description" : "Expression to use for re-ordering the messages, such 
as a header with a sequence number.",
         "title" : "Expression",
-        "required" : true,
         "displayName" : "Expression",
         "kind" : "expression",
         "index" : 4,
-        "group" : "common"
+        "group" : "common",
+        "required" : true
       }, {
         "name" : "batchConfig",
         "type" : "object",
@@ -9401,253 +9401,203 @@
       }, {
         "name" : "asn1",
         "type" : "object",
-        "ref" : "aSN1",
-        "required" : true
+        "ref" : "aSN1"
       }, {
         "name" : "avro",
         "type" : "object",
-        "ref" : "avro",
-        "required" : true
+        "ref" : "avro"
       }, {
         "name" : "barcode",
         "type" : "object",
-        "ref" : "barcode",
-        "required" : true
+        "ref" : "barcode"
       }, {
         "name" : "base64",
         "type" : "object",
-        "ref" : "base64",
-        "required" : true
+        "ref" : "base64"
       }, {
         "name" : "beanio",
         "type" : "object",
-        "ref" : "beanio",
-        "required" : true
+        "ref" : "beanio"
       }, {
         "name" : "bindy",
         "type" : "object",
-        "ref" : "bindy",
-        "required" : true
+        "ref" : "bindy"
       }, {
         "name" : "cbor",
         "type" : "object",
-        "ref" : "cBOR",
-        "required" : true
+        "ref" : "cBOR"
       }, {
         "name" : "crypto",
         "type" : "object",
-        "ref" : "crypto",
-        "required" : true
+        "ref" : "crypto"
       }, {
         "name" : "csv",
         "type" : "object",
-        "ref" : "csv",
-        "required" : true
+        "ref" : "csv"
       }, {
         "name" : "custom",
         "type" : "object",
-        "ref" : "custom",
-        "required" : true
+        "ref" : "custom"
       }, {
         "name" : "dfdl",
         "type" : "object",
-        "ref" : "dfdl",
-        "required" : true
+        "ref" : "dfdl"
       }, {
         "name" : "fhirJson",
         "type" : "object",
-        "ref" : "fhirJson",
-        "required" : true
+        "ref" : "fhirJson"
       }, {
         "name" : "fhirXml",
         "type" : "object",
-        "ref" : "fhirXml",
-        "required" : true
+        "ref" : "fhirXml"
       }, {
         "name" : "flatpack",
         "type" : "object",
-        "ref" : "flatpack",
-        "required" : true
+        "ref" : "flatpack"
       }, {
         "name" : "fory",
         "type" : "object",
-        "ref" : "fory",
-        "required" : true
+        "ref" : "fory"
       }, {
         "name" : "grok",
         "type" : "object",
-        "ref" : "grok",
-        "required" : true
+        "ref" : "grok"
       }, {
         "name" : "groovyJson",
         "type" : "object",
-        "ref" : "groovyJSon",
-        "required" : true
+        "ref" : "groovyJSon"
       }, {
         "name" : "groovyXml",
         "type" : "object",
-        "ref" : "groovyXml",
-        "required" : true
+        "ref" : "groovyXml"
       }, {
         "name" : "gzipDeflater",
         "type" : "object",
-        "ref" : "gzipDeflater",
-        "required" : true
+        "ref" : "gzipDeflater"
       }, {
         "name" : "hl7",
         "type" : "object",
-        "ref" : "hL7",
-        "required" : true
+        "ref" : "hL7"
       }, {
         "name" : "ical",
         "type" : "object",
-        "ref" : "ical",
-        "required" : true
+        "ref" : "ical"
       }, {
         "name" : "iso8583",
         "type" : "object",
-        "ref" : "iso8583",
-        "required" : true
+        "ref" : "iso8583"
       }, {
         "name" : "jacksonXml",
         "type" : "object",
-        "ref" : "jacksonXML",
-        "required" : true
+        "ref" : "jacksonXML"
       }, {
         "name" : "jaxb",
         "type" : "object",
-        "ref" : "jaxb",
-        "required" : true
+        "ref" : "jaxb"
       }, {
         "name" : "json",
         "type" : "object",
-        "ref" : "json",
-        "required" : true
+        "ref" : "json"
       }, {
         "name" : "jsonApi",
         "type" : "object",
-        "ref" : "jsonApi",
-        "required" : true
+        "ref" : "jsonApi"
       }, {
         "name" : "lzf",
         "type" : "object",
-        "ref" : "lZF",
-        "required" : true
+        "ref" : "lZF"
       }, {
         "name" : "mimeMultipart",
         "type" : "object",
-        "ref" : "mimeMultipart",
-        "required" : true
+        "ref" : "mimeMultipart"
       }, {
         "name" : "ocsf",
         "type" : "object",
-        "ref" : "ocsf",
-        "required" : true
+        "ref" : "ocsf"
       }, {
         "name" : "parquetAvro",
         "type" : "object",
-        "ref" : "parquetAvro",
-        "required" : true
+        "ref" : "parquetAvro"
       }, {
         "name" : "pgp",
         "type" : "object",
-        "ref" : "pGP",
-        "required" : true
+        "ref" : "pGP"
       }, {
         "name" : "pqc",
         "type" : "object",
-        "ref" : "pQC",
-        "required" : true
+        "ref" : "pQC"
       }, {
         "name" : "protobuf",
         "type" : "object",
-        "ref" : "protobuf",
-        "required" : true
+        "ref" : "protobuf"
       }, {
         "name" : "rss",
         "type" : "object",
-        "ref" : "rss",
-        "required" : true
+        "ref" : "rss"
       }, {
         "name" : "smooks",
         "type" : "object",
-        "ref" : "smooks",
-        "required" : true
+        "ref" : "smooks"
       }, {
         "name" : "soap",
         "type" : "object",
-        "ref" : "soap",
-        "required" : true
+        "ref" : "soap"
       }, {
         "name" : "swiftMt",
         "type" : "object",
-        "ref" : "swiftMt",
-        "required" : true
+        "ref" : "swiftMt"
       }, {
         "name" : "swiftMx",
         "type" : "object",
-        "ref" : "swiftMx",
-        "required" : true
+        "ref" : "swiftMx"
       }, {
         "name" : "syslog",
         "type" : "object",
-        "ref" : "syslog",
-        "required" : true
+        "ref" : "syslog"
       }, {
         "name" : "tarFile",
         "type" : "object",
-        "ref" : "tarFile",
-        "required" : true
+        "ref" : "tarFile"
       }, {
         "name" : "thrift",
         "type" : "object",
-        "ref" : "thrift",
-        "required" : true
+        "ref" : "thrift"
       }, {
         "name" : "toon",
         "type" : "object",
-        "ref" : "toon",
-        "required" : true
+        "ref" : "toon"
       }, {
         "name" : "ubl",
         "type" : "object",
-        "ref" : "ubl",
-        "required" : true
+        "ref" : "ubl"
       }, {
         "name" : "univocityCsv",
         "type" : "object",
-        "ref" : "uniVocityCsv",
-        "required" : true
+        "ref" : "uniVocityCsv"
       }, {
         "name" : "univocityFixed",
         "type" : "object",
-        "ref" : "uniVocityFixed",
-        "required" : true
+        "ref" : "uniVocityFixed"
       }, {
         "name" : "univocityTsv",
         "type" : "object",
-        "ref" : "uniVocityTsv",
-        "required" : true
+        "ref" : "uniVocityTsv"
       }, {
         "name" : "xmlSecurity",
         "type" : "object",
-        "ref" : "xMLSecurity",
-        "required" : true
+        "ref" : "xMLSecurity"
       }, {
         "name" : "yaml",
         "type" : "object",
-        "ref" : "yAML",
-        "required" : true
+        "ref" : "yAML"
       }, {
         "name" : "zipDeflater",
         "type" : "object",
-        "ref" : "zipDeflater",
-        "required" : true
+        "ref" : "zipDeflater"
       }, {
         "name" : "zipFile",
         "type" : "object",
-        "ref" : "zipFile",
-        "required" : true
+        "ref" : "zipFile"
       } ]
     },
     "validate" : {
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/MarshalDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/MarshalDefinition.java
index 0809bb2ff669..806d55f742ab 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/MarshalDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/MarshalDefinition.java
@@ -136,7 +136,8 @@ public class MarshalDefinition extends 
NoOutputDefinition<MarshalDefinition> imp
             @XmlElement(name = "yaml", type = YAMLDataFormat.class),
             @XmlElement(name = "zipDeflater", type = 
ZipDeflaterDataFormat.class),
             @XmlElement(name = "zipFile", type = ZipFileDataFormat.class) })
-    @Metadata(description = "The data format to use for marshalling the 
message body into a specific format such as JSON, XML, CSV, Avro, Protobuf, 
etc.")
+    @Metadata(required = true,
+              description = "The data format to use for marshalling the 
message body into a specific format such as JSON, XML, CSV, Avro, Protobuf, 
etc.")
     private DataFormatDefinition dataFormatType;
     @XmlAttribute
     @Metadata(description = "To use a variable as the source for the message 
body to send. This makes it handy to use variables for user data and to easily 
control what data to use for sending and receiving.")
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 4599b4fde0a0..1535f6c08263 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
@@ -9461,62 +9461,62 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
             description = "Serializes the message body into a specific data 
format such as JSON, XML, CSV, or Protobuf for transmission or storage",
             deprecated = false,
             properties = {
-                    @YamlProperty(name = "asn1", type = 
"object:org.apache.camel.model.dataformat.ASN1DataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "avro", type = 
"object:org.apache.camel.model.dataformat.AvroDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "barcode", type = 
"object:org.apache.camel.model.dataformat.BarcodeDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "base64", type = 
"object:org.apache.camel.model.dataformat.Base64DataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "beanio", type = 
"object:org.apache.camel.model.dataformat.BeanioDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "bindy", type = 
"object:org.apache.camel.model.dataformat.BindyDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "cbor", type = 
"object:org.apache.camel.model.dataformat.CBORDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "crypto", type = 
"object:org.apache.camel.model.dataformat.CryptoDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "csv", type = 
"object:org.apache.camel.model.dataformat.CsvDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "custom", type = 
"object:org.apache.camel.model.dataformat.CustomDataFormat", oneOf = 
"dataFormatType"),
+                    @YamlProperty(name = "asn1", type = 
"object:org.apache.camel.model.dataformat.ASN1DataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "avro", type = 
"object:org.apache.camel.model.dataformat.AvroDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "barcode", type = 
"object:org.apache.camel.model.dataformat.BarcodeDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "base64", type = 
"object:org.apache.camel.model.dataformat.Base64DataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "beanio", type = 
"object:org.apache.camel.model.dataformat.BeanioDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "bindy", type = 
"object:org.apache.camel.model.dataformat.BindyDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "cbor", type = 
"object:org.apache.camel.model.dataformat.CBORDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "crypto", type = 
"object:org.apache.camel.model.dataformat.CryptoDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "csv", type = 
"object:org.apache.camel.model.dataformat.CsvDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "custom", type = 
"object:org.apache.camel.model.dataformat.CustomDataFormat", required = true, 
oneOf = "dataFormatType"),
                     @YamlProperty(name = "description", type = "string", 
description = "The description for this node", displayName = "Description"),
-                    @YamlProperty(name = "dfdl", type = 
"object:org.apache.camel.model.dataformat.DfdlDataFormat", oneOf = 
"dataFormatType"),
+                    @YamlProperty(name = "dfdl", type = 
"object:org.apache.camel.model.dataformat.DfdlDataFormat", required = true, 
oneOf = "dataFormatType"),
                     @YamlProperty(name = "disabled", type = "boolean", 
defaultValue = "false", description = "Whether to disable this EIP from the 
route during build time. Once an EIP has been disabled then it cannot be 
enabled later at runtime.", displayName = "Disabled"),
-                    @YamlProperty(name = "fhirJson", type = 
"object:org.apache.camel.model.dataformat.FhirJsonDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "fhirXml", type = 
"object:org.apache.camel.model.dataformat.FhirXmlDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "flatpack", type = 
"object:org.apache.camel.model.dataformat.FlatpackDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "fory", type = 
"object:org.apache.camel.model.dataformat.ForyDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "grok", type = 
"object:org.apache.camel.model.dataformat.GrokDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "groovyJson", type = 
"object:org.apache.camel.model.dataformat.GroovyJSonDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "groovyXml", type = 
"object:org.apache.camel.model.dataformat.GroovyXmlDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "gzipDeflater", type = 
"object:org.apache.camel.model.dataformat.GzipDeflaterDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "hl7", type = 
"object:org.apache.camel.model.dataformat.HL7DataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "ical", type = 
"object:org.apache.camel.model.dataformat.IcalDataFormat", oneOf = 
"dataFormatType"),
+                    @YamlProperty(name = "fhirJson", type = 
"object:org.apache.camel.model.dataformat.FhirJsonDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "fhirXml", type = 
"object:org.apache.camel.model.dataformat.FhirXmlDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "flatpack", type = 
"object:org.apache.camel.model.dataformat.FlatpackDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "fory", type = 
"object:org.apache.camel.model.dataformat.ForyDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "grok", type = 
"object:org.apache.camel.model.dataformat.GrokDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "groovyJson", type = 
"object:org.apache.camel.model.dataformat.GroovyJSonDataFormat", required = 
true, oneOf = "dataFormatType"),
+                    @YamlProperty(name = "groovyXml", type = 
"object:org.apache.camel.model.dataformat.GroovyXmlDataFormat", required = 
true, oneOf = "dataFormatType"),
+                    @YamlProperty(name = "gzipDeflater", type = 
"object:org.apache.camel.model.dataformat.GzipDeflaterDataFormat", required = 
true, oneOf = "dataFormatType"),
+                    @YamlProperty(name = "hl7", type = 
"object:org.apache.camel.model.dataformat.HL7DataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "ical", type = 
"object:org.apache.camel.model.dataformat.IcalDataFormat", required = true, 
oneOf = "dataFormatType"),
                     @YamlProperty(name = "id", type = "string", description = 
"The id of this node", displayName = "Id"),
-                    @YamlProperty(name = "iso8583", type = 
"object:org.apache.camel.model.dataformat.Iso8583DataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "jacksonXml", type = 
"object:org.apache.camel.model.dataformat.JacksonXMLDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "jaxb", type = 
"object:org.apache.camel.model.dataformat.JaxbDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "json", type = 
"object:org.apache.camel.model.dataformat.JsonDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "jsonApi", type = 
"object:org.apache.camel.model.dataformat.JsonApiDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "lzf", type = 
"object:org.apache.camel.model.dataformat.LZFDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "mimeMultipart", type = 
"object:org.apache.camel.model.dataformat.MimeMultipartDataFormat", oneOf = 
"dataFormatType"),
+                    @YamlProperty(name = "iso8583", type = 
"object:org.apache.camel.model.dataformat.Iso8583DataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "jacksonXml", type = 
"object:org.apache.camel.model.dataformat.JacksonXMLDataFormat", required = 
true, oneOf = "dataFormatType"),
+                    @YamlProperty(name = "jaxb", type = 
"object:org.apache.camel.model.dataformat.JaxbDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "json", type = 
"object:org.apache.camel.model.dataformat.JsonDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "jsonApi", type = 
"object:org.apache.camel.model.dataformat.JsonApiDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "lzf", type = 
"object:org.apache.camel.model.dataformat.LZFDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "mimeMultipart", type = 
"object:org.apache.camel.model.dataformat.MimeMultipartDataFormat", required = 
true, oneOf = "dataFormatType"),
                     @YamlProperty(name = "note", type = "string", description 
= "The note for this node", displayName = "Note"),
-                    @YamlProperty(name = "ocsf", type = 
"object:org.apache.camel.model.dataformat.OcsfDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "parquetAvro", type = 
"object:org.apache.camel.model.dataformat.ParquetAvroDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "pgp", type = 
"object:org.apache.camel.model.dataformat.PGPDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "pqc", type = 
"object:org.apache.camel.model.dataformat.PQCDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "protobuf", type = 
"object:org.apache.camel.model.dataformat.ProtobufDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "rss", type = 
"object:org.apache.camel.model.dataformat.RssDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "smooks", type = 
"object:org.apache.camel.model.dataformat.SmooksDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "soap", type = 
"object:org.apache.camel.model.dataformat.SoapDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "swiftMt", type = 
"object:org.apache.camel.model.dataformat.SwiftMtDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "swiftMx", type = 
"object:org.apache.camel.model.dataformat.SwiftMxDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "syslog", type = 
"object:org.apache.camel.model.dataformat.SyslogDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "tarFile", type = 
"object:org.apache.camel.model.dataformat.TarFileDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "thrift", type = 
"object:org.apache.camel.model.dataformat.ThriftDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "toon", type = 
"object:org.apache.camel.model.dataformat.ToonDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "ubl", type = 
"object:org.apache.camel.model.dataformat.UblDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "univocityCsv", type = 
"object:org.apache.camel.model.dataformat.UniVocityCsvDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "univocityFixed", type = 
"object:org.apache.camel.model.dataformat.UniVocityFixedDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "univocityTsv", type = 
"object:org.apache.camel.model.dataformat.UniVocityTsvDataFormat", oneOf = 
"dataFormatType"),
+                    @YamlProperty(name = "ocsf", type = 
"object:org.apache.camel.model.dataformat.OcsfDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "parquetAvro", type = 
"object:org.apache.camel.model.dataformat.ParquetAvroDataFormat", required = 
true, oneOf = "dataFormatType"),
+                    @YamlProperty(name = "pgp", type = 
"object:org.apache.camel.model.dataformat.PGPDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "pqc", type = 
"object:org.apache.camel.model.dataformat.PQCDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "protobuf", type = 
"object:org.apache.camel.model.dataformat.ProtobufDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "rss", type = 
"object:org.apache.camel.model.dataformat.RssDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "smooks", type = 
"object:org.apache.camel.model.dataformat.SmooksDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "soap", type = 
"object:org.apache.camel.model.dataformat.SoapDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "swiftMt", type = 
"object:org.apache.camel.model.dataformat.SwiftMtDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "swiftMx", type = 
"object:org.apache.camel.model.dataformat.SwiftMxDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "syslog", type = 
"object:org.apache.camel.model.dataformat.SyslogDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "tarFile", type = 
"object:org.apache.camel.model.dataformat.TarFileDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "thrift", type = 
"object:org.apache.camel.model.dataformat.ThriftDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "toon", type = 
"object:org.apache.camel.model.dataformat.ToonDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "ubl", type = 
"object:org.apache.camel.model.dataformat.UblDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "univocityCsv", type = 
"object:org.apache.camel.model.dataformat.UniVocityCsvDataFormat", required = 
true, oneOf = "dataFormatType"),
+                    @YamlProperty(name = "univocityFixed", type = 
"object:org.apache.camel.model.dataformat.UniVocityFixedDataFormat", required = 
true, oneOf = "dataFormatType"),
+                    @YamlProperty(name = "univocityTsv", type = 
"object:org.apache.camel.model.dataformat.UniVocityTsvDataFormat", required = 
true, oneOf = "dataFormatType"),
                     @YamlProperty(name = "variableReceive", type = "string", 
description = "To use a variable to store the received message body (only body, 
not headers). This makes it handy to use variables for user data and to easily 
control what data to use for sending and receiving.", displayName = "Variable 
Receive"),
                     @YamlProperty(name = "variableSend", type = "string", 
description = "To use a variable as the source for the message body to send. 
This makes it handy to use variables for user data and to easily control what 
data to use for sending and receiving.", displayName = "Variable Send"),
-                    @YamlProperty(name = "xmlSecurity", type = 
"object:org.apache.camel.model.dataformat.XMLSecurityDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "yaml", type = 
"object:org.apache.camel.model.dataformat.YAMLDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "zipDeflater", type = 
"object:org.apache.camel.model.dataformat.ZipDeflaterDataFormat", oneOf = 
"dataFormatType"),
-                    @YamlProperty(name = "zipFile", type = 
"object:org.apache.camel.model.dataformat.ZipFileDataFormat", oneOf = 
"dataFormatType")
+                    @YamlProperty(name = "xmlSecurity", type = 
"object:org.apache.camel.model.dataformat.XMLSecurityDataFormat", required = 
true, oneOf = "dataFormatType"),
+                    @YamlProperty(name = "yaml", type = 
"object:org.apache.camel.model.dataformat.YAMLDataFormat", required = true, 
oneOf = "dataFormatType"),
+                    @YamlProperty(name = "zipDeflater", type = 
"object:org.apache.camel.model.dataformat.ZipDeflaterDataFormat", required = 
true, oneOf = "dataFormatType"),
+                    @YamlProperty(name = "zipFile", type = 
"object:org.apache.camel.model.dataformat.ZipFileDataFormat", required = true, 
oneOf = "dataFormatType")
             }
     )
     public static class MarshalDefinitionDeserializer extends 
YamlDeserializerBase<MarshalDefinition> {
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
index 2fd42f59817f..914e3ef37ac8 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
@@ -345,7 +345,12 @@ public class GenerateYamlSchemaMojo extends 
GenerateYamlSupportMojo {
                     propertyWrapItem,
                     additionalProperties);
 
-            if (propertyRequired) {
+            // A property that belongs to a oneOf group (e.g. the data formats 
of marshal/unmarshal, or the
+            // languages of an expression) is only required as part of 
choosing one of the alternatives, not
+            // unconditionally. In canonical mode the oneOf grouping itself is 
dropped (isInOneOf is always
+            // false), so without this guard every alternative would end up in 
the flat "required" list,
+            // demanding all of them at once instead of exactly one.
+            if (propertyRequired && StringUtils.isEmpty(propertyOneOf)) {
                 definition.withArray("required").add(propertyName);
             }
         }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/pom.xml 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/pom.xml
index ca8fa7c84c31..4392c785b763 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/pom.xml
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/pom.xml
@@ -54,6 +54,10 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-stub</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-catalog</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.networknt</groupId>
             <artifactId>json-schema-validator</artifactId>
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 efc833acb421..5b10bcc80123 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
@@ -20,6 +20,8 @@ import java.io.File;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -40,6 +42,11 @@ import com.networknt.schema.SpecificationVersion;
 import com.networknt.schema.dialect.Dialect;
 import com.networknt.schema.dialect.Dialects;
 import com.networknt.schema.keyword.NonValidationKeyword;
+import com.networknt.schema.path.NodePath;
+import com.networknt.schema.path.PathType;
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.tooling.model.EipModel;
 
 /**
  * YAML DSL validator that tooling can use to validate Camel source files if 
they can be parsed and are valid according
@@ -50,9 +57,27 @@ public class YamlValidator {
     private static final String LOCATION = "/schema/camelYamlDsl.json";
     private static final String LOCATION_CANONICAL = 
"/schema/camelYamlDsl-canonical.json";
 
+    /**
+     * A handful of "pick exactly one" EIP option groups (see {@link 
EipModel.EipOptionModel#getOneOfs()}) flatten their
+     * alternatives directly onto a specific host node instead of appearing 
under a wrapper key named after the option
+     * itself (that's how "expression" works). The canonical schema cannot 
express this "exactly one of" cardinality (it
+     * has no oneOf/anyOf constructs), so {@link #checkOneOfCardinality} 
re-checks it here, driven by the same catalog
+     * metadata the classic schema is generated from.
+     */
+    private static final Map<String, Set<String>> FLATTENED_HOSTS = Map.of(
+            "dataFormatType", Set.of("marshal", "unmarshal"),
+            "errorHandlerType", Set.of("errorHandler"),
+            "tokenizerImplementation", Set.of("tokenizer"),
+            "resequencerConfig", Set.of("resequence"),
+            "loadBalancerType", Set.of("loadBalance"));
+
     private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
     private final boolean canonical;
     private Schema schema;
+    private Map<String, OneOfGroup> oneOfGroups;
+
+    private record OneOfGroup(Set<String> alternatives, boolean required) {
+    }
 
     public YamlValidator() {
         this(false);
@@ -72,7 +97,7 @@ public class YamlValidator {
         }
         try {
             var target = mapper.readTree(file);
-            return filterOneOfNoise(new ArrayList<>(schema.validate(target)));
+            return validate(target);
         } catch (Exception e) {
             return List.of(parseError(e));
         }
@@ -84,12 +109,20 @@ public class YamlValidator {
         }
         try {
             var target = mapper.readTree(content);
-            return filterOneOfNoise(new ArrayList<>(schema.validate(target)));
+            return validate(target);
         } catch (Exception e) {
             return List.of(parseError(e));
         }
     }
 
+    private List<Error> validate(JsonNode target) {
+        var errors = filterOneOfNoise(new 
ArrayList<>(schema.validate(target)));
+        if (canonical) {
+            checkOneOfCardinality(target, new NodePath(PathType.JSON_POINTER), 
errors);
+        }
+        return errors;
+    }
+
     /**
      * Filters noise from {@code oneOf} validation. When a {@code oneOf} has N 
branches and none match, the validator
      * reports errors from ALL branches — producing dozens of "required 
property 'X' not found" messages for branches
@@ -221,6 +254,88 @@ public class YamlValidator {
                         || "maxItems".equals(keyword));
     }
 
+    /**
+     * Recursively walks the parsed YAML tree looking for the host 
nodes/wrapper keys of a "pick exactly one" option
+     * group, and reports a synthetic error when zero (for a required group) 
or more than one alternative is present.
+     */
+    private void checkOneOfCardinality(JsonNode node, NodePath path, 
List<Error> errors) {
+        if (node.isObject()) {
+            Iterator<Map.Entry<String, JsonNode>> it = node.fields();
+            while (it.hasNext()) {
+                Map.Entry<String, JsonNode> entry = it.next();
+                String key = entry.getKey();
+                JsonNode value = entry.getValue();
+                NodePath childPath = path.append(key);
+                if (value.isObject()) {
+                    for (Map.Entry<String, Set<String>> hostEntry : 
FLATTENED_HOSTS.entrySet()) {
+                        if (hostEntry.getValue().contains(key)) {
+                            reportIfInvalidCardinality(value, 
oneOfGroups.get(hostEntry.getKey()), childPath, errors);
+                        }
+                    }
+                    if (!FLATTENED_HOSTS.containsKey(key)) {
+                        reportIfInvalidCardinality(value, 
oneOfGroups.get(key), childPath, errors);
+                    }
+                }
+                checkOneOfCardinality(value, childPath, errors);
+            }
+        } else if (node.isArray()) {
+            for (int i = 0; i < node.size(); i++) {
+                checkOneOfCardinality(node.get(i), path.append(i), errors);
+            }
+        }
+    }
+
+    private static void reportIfInvalidCardinality(JsonNode container, 
OneOfGroup group, NodePath path, List<Error> errors) {
+        if (group == null) {
+            return;
+        }
+        List<String> found = new ArrayList<>();
+        Iterator<String> names = container.fieldNames();
+        while (names.hasNext()) {
+            String name = names.next();
+            if (group.alternatives().contains(name)) {
+                found.add(name);
+            }
+        }
+        if (found.isEmpty() && group.required()) {
+            errors.add(Error.builder()
+                    .keyword("oneOf")
+                    .instanceLocation(path)
+                    .message("must have exactly one of " + 
group.alternatives() + " but found none")
+                    .build());
+        } else if (found.size() > 1) {
+            errors.add(Error.builder()
+                    .keyword("oneOf")
+                    .instanceLocation(path)
+                    .message("must have exactly one of " + 
group.alternatives() + " but found: " + found)
+                    .build());
+        }
+    }
+
+    /**
+     * Builds the "pick exactly one" option groups from the Camel catalog's 
EIP model metadata - the same metadata the
+     * classic (non-canonical) schema's oneOf groups are generated from. Only 
object-typed options are considered;
+     * array-typed options (e.g. "outputs") use "oneOf" to mean "each element 
is one of these types", not "exactly one
+     * of these sibling keys must be present".
+     */
+    private static Map<String, OneOfGroup> loadOneOfGroups() {
+        Map<String, OneOfGroup> groups = new HashMap<>();
+        CamelCatalog catalog = new DefaultCamelCatalog();
+        for (String name : catalog.findModelNames()) {
+            EipModel model = catalog.eipModel(name);
+            if (model == null) {
+                continue;
+            }
+            for (EipModel.EipOptionModel option : model.getOptions()) {
+                List<String> oneOfs = option.getOneOfs();
+                if (oneOfs != null && !oneOfs.isEmpty() && 
"object".equals(option.getType())) {
+                    groups.putIfAbsent(option.getName(), new OneOfGroup(new 
LinkedHashSet<>(oneOfs), option.isRequired()));
+                }
+            }
+        }
+        return groups;
+    }
+
     private static Error parseError(Exception e) {
         String msg = e.getClass().getName() + ": " + e.getMessage();
         return Error.builder()
@@ -248,6 +363,10 @@ public class YamlValidator {
         // Use a proper URI for the schema location to ensure $ref resolution 
works
         var schemaLocation = SchemaLocation.of(location);
         schema = schemaRegistry.getSchema(schemaLocation, model);
+
+        if (canonical) {
+            oneOfGroups = loadOneOfGroups();
+        }
     }
 
     private static Dialect getBaseDialect(SpecificationVersion version) {
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 0c5578139dc4..45c5c17de9e3 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
@@ -22,6 +22,8 @@ import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 public class YamlCanonicalValidatorTest {
 
     private static YamlValidator canonicalValidator;
@@ -96,4 +98,68 @@ public class YamlCanonicalValidatorTest {
         Assertions.assertFalse(canonicalReport.isEmpty(),
                 "foo.yaml uses implicit forms and should fail canonical 
validation");
     }
+
+    @Test
+    public void 
testUnmarshalMarshalWithSingleDataFormatPassesCanonicalValidation() throws 
Exception {
+        // CAMEL-24482: a single data format (e.g. json) on unmarshal/marshal 
must not require
+        // every other data format to also be present.
+        var report = canonicalValidator.validate(new 
File("src/test/resources/canonical-valid-dataformat.yaml"));
+        assertThat(report).as("Single data format should pass canonical 
validation but got: %s", report).isEmpty();
+    }
+
+    @Test
+    public void 
testUnmarshalMarshalWithSingleDataFormatPassesClassicValidation() throws 
Exception {
+        var report = classicValidator.validate(new 
File("src/test/resources/canonical-valid-dataformat.yaml"));
+        assertThat(report).as("Single data format should pass classic 
validation but got: %s", report).isEmpty();
+    }
+
+    @Test
+    public void testUnmarshalWithoutDataFormatFailsCanonicalValidation() 
throws Exception {
+        // The canonical schema itself has no oneOf/anyOf constructs to 
express "exactly one of these N
+        // properties is required", so YamlValidator re-checks this 
cardinality itself, driven by the same
+        // catalog "oneOf" metadata the classic schema is generated from.
+        var report = canonicalValidator.validate(new 
File("src/test/resources/canonical-invalid-missing-dataformat.yaml"));
+        assertThat(report).as("unmarshal without a data format should fail 
canonical validation").isNotEmpty();
+    }
+
+    @Test
+    public void testUnmarshalWithoutDataFormatFailsClassicValidation() throws 
Exception {
+        // Unlike canonical mode, the classic schema keeps its oneOf group and 
still requires exactly
+        // one data format to be chosen.
+        var report = classicValidator.validate(new 
File("src/test/resources/canonical-invalid-missing-dataformat.yaml"));
+        assertThat(report).as("unmarshal without a data format should fail 
classic validation").isNotEmpty();
+    }
+
+    @Test
+    public void 
testResequenceWithoutBatchOrStreamConfigFailsCanonicalValidation() throws 
Exception {
+        // The "exactly one of" cardinality check is generic, not 
special-cased to marshal/unmarshal:
+        // resequence must pick exactly one of batchConfig/streamConfig too.
+        var route = """
+                - route:
+                    from:
+                      uri: "direct:start"
+                      steps:
+                        - resequence:
+                            expression:
+                              simple:
+                                expression: "${header.seqnum}"
+                """;
+        var report = canonicalValidator.validate(route);
+        assertThat(report).as("resequence without batchConfig/streamConfig 
should fail canonical validation").isNotEmpty();
+    }
+
+    @Test
+    public void testLoadBalanceWithTwoLoadBalancersFailsCanonicalValidation() 
throws Exception {
+        var route = """
+                - route:
+                    from:
+                      uri: "direct:start"
+                      steps:
+                        - loadBalance:
+                            roundRobinLoadBalancer: {}
+                            randomLoadBalancer: {}
+                """;
+        var report = canonicalValidator.validate(route);
+        assertThat(report).as("loadBalance with two load balancers should fail 
canonical validation").isNotEmpty();
+    }
 }
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/canonical-invalid-missing-dataformat.yaml
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/canonical-invalid-missing-dataformat.yaml
new file mode 100644
index 000000000000..0fb0c7cd2f19
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/canonical-invalid-missing-dataformat.yaml
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - unmarshal: {}
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/canonical-valid-dataformat.yaml
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/canonical-valid-dataformat.yaml
new file mode 100644
index 000000000000..ccad9e74c408
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/canonical-valid-dataformat.yaml
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - unmarshal:
+            json:
+              id: json-unmarshal
+        - marshal:
+            json:
+              id: json-marshal
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
index 5a117863f229..17dde1a7adf3 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
@@ -3115,7 +3115,7 @@
             "description" : "The property key."
           }
         },
-        "required" : [ "expression", "key" ]
+        "required" : [ "key" ]
       },
       "org.apache.camel.model.RecipientListDefinition" : {
         "title" : "Recipient List",
@@ -3627,8 +3627,7 @@
           "streamConfig" : {
             "$ref" : 
"#/items/definitions/org.apache.camel.model.config.StreamResequencerConfig"
           }
-        },
-        "required" : [ "expression" ]
+        }
       },
       "org.apache.camel.model.Resilience4jConfigurationDefinition" : {
         "title" : "Resilience4j Configuration",
@@ -5767,8 +5766,7 @@
           "zipFile" : {
             "$ref" : 
"#/items/definitions/org.apache.camel.model.dataformat.ZipFileDataFormat"
           }
-        },
-        "required" : [ "asn1", "avro", "barcode", "base64", "beanio", "bindy", 
"cbor", "crypto", "csv", "custom", "dfdl", "fhirJson", "fhirXml", "flatpack", 
"fory", "grok", "groovyJson", "groovyXml", "gzipDeflater", "hl7", "ical", 
"iso8583", "jacksonXml", "jaxb", "json", "jsonApi", "lzf", "mimeMultipart", 
"ocsf", "parquetAvro", "pgp", "pqc", "protobuf", "rss", "smooks", "soap", 
"swiftMt", "swiftMx", "syslog", "tarFile", "thrift", "toon", "ubl", 
"univocityCsv", "univocityFixed", "univocit [...]
+        }
       },
       "org.apache.camel.model.ValidateDefinition" : {
         "title" : "Validate",
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
index 80f73eaa4b1e..9a683bb8bf48 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
@@ -5301,11 +5301,11 @@
         "ref" : "expression",
         "description" : "The property value as an expression.",
         "title" : "Expression",
-        "required" : true,
         "displayName" : "Expression",
         "kind" : "expression",
         "index" : 1,
-        "group" : "common"
+        "group" : "common",
+        "required" : true
       } ]
     },
     "recipientList" : {
@@ -6154,11 +6154,11 @@
         "ref" : "expression",
         "description" : "Expression to use for re-ordering the messages, such 
as a header with a sequence number.",
         "title" : "Expression",
-        "required" : true,
         "displayName" : "Expression",
         "kind" : "expression",
         "index" : 4,
-        "group" : "common"
+        "group" : "common",
+        "required" : true
       }, {
         "name" : "batchConfig",
         "type" : "object",
@@ -9401,253 +9401,203 @@
       }, {
         "name" : "asn1",
         "type" : "object",
-        "ref" : "aSN1",
-        "required" : true
+        "ref" : "aSN1"
       }, {
         "name" : "avro",
         "type" : "object",
-        "ref" : "avro",
-        "required" : true
+        "ref" : "avro"
       }, {
         "name" : "barcode",
         "type" : "object",
-        "ref" : "barcode",
-        "required" : true
+        "ref" : "barcode"
       }, {
         "name" : "base64",
         "type" : "object",
-        "ref" : "base64",
-        "required" : true
+        "ref" : "base64"
       }, {
         "name" : "beanio",
         "type" : "object",
-        "ref" : "beanio",
-        "required" : true
+        "ref" : "beanio"
       }, {
         "name" : "bindy",
         "type" : "object",
-        "ref" : "bindy",
-        "required" : true
+        "ref" : "bindy"
       }, {
         "name" : "cbor",
         "type" : "object",
-        "ref" : "cBOR",
-        "required" : true
+        "ref" : "cBOR"
       }, {
         "name" : "crypto",
         "type" : "object",
-        "ref" : "crypto",
-        "required" : true
+        "ref" : "crypto"
       }, {
         "name" : "csv",
         "type" : "object",
-        "ref" : "csv",
-        "required" : true
+        "ref" : "csv"
       }, {
         "name" : "custom",
         "type" : "object",
-        "ref" : "custom",
-        "required" : true
+        "ref" : "custom"
       }, {
         "name" : "dfdl",
         "type" : "object",
-        "ref" : "dfdl",
-        "required" : true
+        "ref" : "dfdl"
       }, {
         "name" : "fhirJson",
         "type" : "object",
-        "ref" : "fhirJson",
-        "required" : true
+        "ref" : "fhirJson"
       }, {
         "name" : "fhirXml",
         "type" : "object",
-        "ref" : "fhirXml",
-        "required" : true
+        "ref" : "fhirXml"
       }, {
         "name" : "flatpack",
         "type" : "object",
-        "ref" : "flatpack",
-        "required" : true
+        "ref" : "flatpack"
       }, {
         "name" : "fory",
         "type" : "object",
-        "ref" : "fory",
-        "required" : true
+        "ref" : "fory"
       }, {
         "name" : "grok",
         "type" : "object",
-        "ref" : "grok",
-        "required" : true
+        "ref" : "grok"
       }, {
         "name" : "groovyJson",
         "type" : "object",
-        "ref" : "groovyJSon",
-        "required" : true
+        "ref" : "groovyJSon"
       }, {
         "name" : "groovyXml",
         "type" : "object",
-        "ref" : "groovyXml",
-        "required" : true
+        "ref" : "groovyXml"
       }, {
         "name" : "gzipDeflater",
         "type" : "object",
-        "ref" : "gzipDeflater",
-        "required" : true
+        "ref" : "gzipDeflater"
       }, {
         "name" : "hl7",
         "type" : "object",
-        "ref" : "hL7",
-        "required" : true
+        "ref" : "hL7"
       }, {
         "name" : "ical",
         "type" : "object",
-        "ref" : "ical",
-        "required" : true
+        "ref" : "ical"
       }, {
         "name" : "iso8583",
         "type" : "object",
-        "ref" : "iso8583",
-        "required" : true
+        "ref" : "iso8583"
       }, {
         "name" : "jacksonXml",
         "type" : "object",
-        "ref" : "jacksonXML",
-        "required" : true
+        "ref" : "jacksonXML"
       }, {
         "name" : "jaxb",
         "type" : "object",
-        "ref" : "jaxb",
-        "required" : true
+        "ref" : "jaxb"
       }, {
         "name" : "json",
         "type" : "object",
-        "ref" : "json",
-        "required" : true
+        "ref" : "json"
       }, {
         "name" : "jsonApi",
         "type" : "object",
-        "ref" : "jsonApi",
-        "required" : true
+        "ref" : "jsonApi"
       }, {
         "name" : "lzf",
         "type" : "object",
-        "ref" : "lZF",
-        "required" : true
+        "ref" : "lZF"
       }, {
         "name" : "mimeMultipart",
         "type" : "object",
-        "ref" : "mimeMultipart",
-        "required" : true
+        "ref" : "mimeMultipart"
       }, {
         "name" : "ocsf",
         "type" : "object",
-        "ref" : "ocsf",
-        "required" : true
+        "ref" : "ocsf"
       }, {
         "name" : "parquetAvro",
         "type" : "object",
-        "ref" : "parquetAvro",
-        "required" : true
+        "ref" : "parquetAvro"
       }, {
         "name" : "pgp",
         "type" : "object",
-        "ref" : "pGP",
-        "required" : true
+        "ref" : "pGP"
       }, {
         "name" : "pqc",
         "type" : "object",
-        "ref" : "pQC",
-        "required" : true
+        "ref" : "pQC"
       }, {
         "name" : "protobuf",
         "type" : "object",
-        "ref" : "protobuf",
-        "required" : true
+        "ref" : "protobuf"
       }, {
         "name" : "rss",
         "type" : "object",
-        "ref" : "rss",
-        "required" : true
+        "ref" : "rss"
       }, {
         "name" : "smooks",
         "type" : "object",
-        "ref" : "smooks",
-        "required" : true
+        "ref" : "smooks"
       }, {
         "name" : "soap",
         "type" : "object",
-        "ref" : "soap",
-        "required" : true
+        "ref" : "soap"
       }, {
         "name" : "swiftMt",
         "type" : "object",
-        "ref" : "swiftMt",
-        "required" : true
+        "ref" : "swiftMt"
       }, {
         "name" : "swiftMx",
         "type" : "object",
-        "ref" : "swiftMx",
-        "required" : true
+        "ref" : "swiftMx"
       }, {
         "name" : "syslog",
         "type" : "object",
-        "ref" : "syslog",
-        "required" : true
+        "ref" : "syslog"
       }, {
         "name" : "tarFile",
         "type" : "object",
-        "ref" : "tarFile",
-        "required" : true
+        "ref" : "tarFile"
       }, {
         "name" : "thrift",
         "type" : "object",
-        "ref" : "thrift",
-        "required" : true
+        "ref" : "thrift"
       }, {
         "name" : "toon",
         "type" : "object",
-        "ref" : "toon",
-        "required" : true
+        "ref" : "toon"
       }, {
         "name" : "ubl",
         "type" : "object",
-        "ref" : "ubl",
-        "required" : true
+        "ref" : "ubl"
       }, {
         "name" : "univocityCsv",
         "type" : "object",
-        "ref" : "uniVocityCsv",
-        "required" : true
+        "ref" : "uniVocityCsv"
       }, {
         "name" : "univocityFixed",
         "type" : "object",
-        "ref" : "uniVocityFixed",
-        "required" : true
+        "ref" : "uniVocityFixed"
       }, {
         "name" : "univocityTsv",
         "type" : "object",
-        "ref" : "uniVocityTsv",
-        "required" : true
+        "ref" : "uniVocityTsv"
       }, {
         "name" : "xmlSecurity",
         "type" : "object",
-        "ref" : "xMLSecurity",
-        "required" : true
+        "ref" : "xMLSecurity"
       }, {
         "name" : "yaml",
         "type" : "object",
-        "ref" : "yAML",
-        "required" : true
+        "ref" : "yAML"
       }, {
         "name" : "zipDeflater",
         "type" : "object",
-        "ref" : "zipDeflater",
-        "required" : true
+        "ref" : "zipDeflater"
       }, {
         "name" : "zipFile",
         "type" : "object",
-        "ref" : "zipFile",
-        "required" : true
+        "ref" : "zipFile"
       } ]
     },
     "validate" : {
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 42e25c573ed0..c54c9eaa9591 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
@@ -3168,110 +3168,6 @@
                 "$ref" : 
"#/items/definitions/org.apache.camel.model.dataformat.ASN1DataFormat"
               }
             }
-          }, {
-            "not" : {
-              "anyOf" : [ {
-                "required" : [ "asn1" ]
-              }, {
-                "required" : [ "avro" ]
-              }, {
-                "required" : [ "barcode" ]
-              }, {
-                "required" : [ "base64" ]
-              }, {
-                "required" : [ "beanio" ]
-              }, {
-                "required" : [ "bindy" ]
-              }, {
-                "required" : [ "cbor" ]
-              }, {
-                "required" : [ "crypto" ]
-              }, {
-                "required" : [ "csv" ]
-              }, {
-                "required" : [ "custom" ]
-              }, {
-                "required" : [ "dfdl" ]
-              }, {
-                "required" : [ "fhirJson" ]
-              }, {
-                "required" : [ "fhirXml" ]
-              }, {
-                "required" : [ "flatpack" ]
-              }, {
-                "required" : [ "fory" ]
-              }, {
-                "required" : [ "grok" ]
-              }, {
-                "required" : [ "groovyJson" ]
-              }, {
-                "required" : [ "groovyXml" ]
-              }, {
-                "required" : [ "gzipDeflater" ]
-              }, {
-                "required" : [ "hl7" ]
-              }, {
-                "required" : [ "ical" ]
-              }, {
-                "required" : [ "iso8583" ]
-              }, {
-                "required" : [ "jacksonXml" ]
-              }, {
-                "required" : [ "jaxb" ]
-              }, {
-                "required" : [ "json" ]
-              }, {
-                "required" : [ "jsonApi" ]
-              }, {
-                "required" : [ "lzf" ]
-              }, {
-                "required" : [ "mimeMultipart" ]
-              }, {
-                "required" : [ "ocsf" ]
-              }, {
-                "required" : [ "parquetAvro" ]
-              }, {
-                "required" : [ "pgp" ]
-              }, {
-                "required" : [ "pqc" ]
-              }, {
-                "required" : [ "protobuf" ]
-              }, {
-                "required" : [ "rss" ]
-              }, {
-                "required" : [ "smooks" ]
-              }, {
-                "required" : [ "soap" ]
-              }, {
-                "required" : [ "swiftMt" ]
-              }, {
-                "required" : [ "swiftMx" ]
-              }, {
-                "required" : [ "syslog" ]
-              }, {
-                "required" : [ "tarFile" ]
-              }, {
-                "required" : [ "thrift" ]
-              }, {
-                "required" : [ "toon" ]
-              }, {
-                "required" : [ "ubl" ]
-              }, {
-                "required" : [ "univocityCsv" ]
-              }, {
-                "required" : [ "univocityFixed" ]
-              }, {
-                "required" : [ "univocityTsv" ]
-              }, {
-                "required" : [ "xmlSecurity" ]
-              }, {
-                "required" : [ "yaml" ]
-              }, {
-                "required" : [ "zipDeflater" ]
-              }, {
-                "required" : [ "zipFile" ]
-              } ]
-            }
           }, {
             "type" : "object",
             "required" : [ "avro" ],
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/MarshalTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/MarshalTest.groovy
index 7ce4877cb22b..18b80c7b0bdb 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/MarshalTest.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/MarshalTest.groovy
@@ -108,17 +108,21 @@ class MarshalTest extends YamlTestSupport {
         }
     }
 
-    def "no dataformat"() {
+    def "Error: no dataformat"() {
         when:
         var route = '''
                     - from:
                         uri: "direct:start"
-                        steps:    
+                        steps:
                           - marshal: {}
                           - to: "mock:result"
             '''
-        loadRoutes(route)
         then:
-        context.routeDefinitions.size() == 1
+        try {
+            loadRoutes(route)
+            Assertions.fail("Should have thrown exception")
+        } catch (IllegalArgumentException e) {
+            Assertions.assertTrue(e.getMessage().contains("0 are valid"), 
e.getMessage())
+        }
     }
 }

Reply via email to