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

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

commit 639e282ef866c440446e4a4fdcb42a3e1f6afa91
Author: Luigi De Masi <[email protected]>
AuthorDate: Wed Sep 30 18:34:47 2026 +0200

    CAMEL-25178: Improve Switch EIP discoverability and DSL support
    
    Add Switch documentation, catalog aliases and intent mappings, and
    resource-based XML, YAML and Spring round-trip coverage.
    
    Support Endpoint DSL destinations while preserving builders through copying,
    JAXB dumping and reification. Show case values and destinations in route
    structure output while keeping the fallback processor type and identity.
    
    Co-authored-by: Codex <[email protected]>
    Signed-off-by: Luigi De Masi <[email protected]>
---
 .../org/apache/camel/catalog/docs/ai-patterns.adoc |   6 +-
 .../org/apache/camel/catalog/docs/choice-eip.adoc  |   6 +
 .../docs/enterprise-integration-patterns.adoc      |   4 +
 .../org/apache/camel/catalog/docs/switch-eip.adoc  | 135 ++++++++++++++-------
 .../org/apache/camel/catalog/models/switch.json    |   1 +
 .../camel/catalog/schemas/camelYamlDsl-model.json  |   1 +
 .../camel/spring/processor/SpringSwitchTest.java   |  51 ++++++++
 .../camel/spring/processor/SpringSwitchTest.xml    |  48 ++++++++
 .../SwitchRouteStructureDevConsoleTest.java        |  89 ++++++++++++++
 .../main/docs/modules/eips/pages/ai-patterns.adoc  |   6 +-
 .../main/docs/modules/eips/pages/choice-eip.adoc   |   6 +
 .../pages/enterprise-integration-patterns.adoc     |   4 +
 .../main/docs/modules/eips/pages/switch-eip.adoc   | 135 ++++++++++++++-------
 .../camel/impl/DefaultModelToStructureDumper.java  |  13 +-
 .../META-INF/org/apache/camel/model/switch.json    |   1 +
 .../apache/camel/model/SwitchCaseDefinition.java   |  31 ++++-
 .../org/apache/camel/model/SwitchDefinition.java   |  33 ++++-
 .../camel/model/SwitchOtherwiseDefinition.java     |  26 +++-
 .../apache/camel/model/SwitchDefinitionTest.java   |  12 ++
 .../org/apache/camel/reifier/SwitchReifier.java    |   8 +-
 .../camel/processor/SwitchValidationTest.java      |   2 +-
 .../org/apache/camel/java/in/LwJavaParserTest.java |  29 +++++
 core/camel-xml-io/src/test/resources/switch.xml    |  40 ++++++
 .../java/org/apache/camel/xml/jaxb/JaxbHelper.java |  17 +++
 .../camel/yaml/io/YamlPrinterRoundTripTest.java    |  21 ++++
 .../org/apache/camel/yaml/out/XmlToYamlTest.java   |  31 +++++
 .../apache/camel/yaml/out/YamlModelWriterTest.java |  16 +++
 .../src/test/resources/yaml-route-switch.yaml      |  38 ++++++
 .../builder/endpoint/SwitchEndpointDslTest.java    | 106 ++++++++++++++++
 .../dsl/jbang/core/commands/ai/eip-samples.json    |   4 +-
 .../dsl/jbang/core/commands/ai/CatalogSamples.java |   4 +
 .../dsl/jbang/core/commands/ai/ToolRegistry.java   |   4 +-
 .../jbang/core/commands/ai/CatalogSamplesTest.java |  21 ++++
 .../jbang/core/commands/mcp/PromptDefinitions.java |   2 +-
 .../camel/dsl/yaml/validator/SwitchSchemaTest.java |   9 ++
 .../resources/schema/camelYamlDsl-model.json       |   1 +
 .../java/org/apache/camel/dsl/yaml/SwitchTest.java |  47 +------
 .../src/test/resources/switch.camel.yaml           |  56 +++++++++
 38 files changed, 909 insertions(+), 155 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-patterns.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-patterns.adoc
index a783c5882f69..d77c72900306 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-patterns.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ai-patterns.adoc
@@ -48,7 +48,7 @@ See also the full 
xref:enterprise-integration-patterns.adoc[Enterprise Integrati
 |Discard messages that do not match a predicate.
 
 |Router / Dispatch
-|xref:choice-eip.adoc[Content-Based Router], 
xref:dynamicRouter-eip.adoc[Dynamic Router]
+|xref:choice-eip.adoc[Content-Based Router], xref:switch-eip.adoc[Switch], 
xref:dynamicRouter-eip.adoc[Dynamic Router]
 |Route messages to different destinations based on content or rules.
 |===
 
@@ -208,6 +208,10 @@ See also the full 
xref:enterprise-integration-patterns.adoc[Enterprise Integrati
 |===
 |Term |Camel Component / EIP |Description
 
+|Classify / Intent routing
+|xref:languages:semantic-language.adoc[Semantic], xref:switch-eip.adoc[Switch]
+|Evaluate a Semantic choice question once and dispatch its category string 
through literal Switch cases.
+
 |Agent-to-Agent / A2A
 |xref:ROOT:a2a-component.adoc[A2A]
 |Google's Agent-to-Agent protocol for communication between AI agents.
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/choice-eip.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/choice-eip.adoc
index 0e3d67c6ba1a..c9f0645698c2 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/choice-eip.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/choice-eip.adoc
@@ -13,6 +13,12 @@ on the contents of the message exchanges.
 
 image::eip/ContentBasedRouter.gif[image]
 
+TIP: For a decision table based on the literal value of one expression, use
+xref:switch-eip.adoc[Switch]. Each Switch case sends to one endpoint, with no 
inline
+processing steps. Branches can be separate routes linked through
+xref:ROOT:direct-component.adoc[Direct] or xref:ROOT:seda-component.adoc[SEDA].
+Choice supports predicates, ranges, and inline processing steps.
+
 == Choice options
 
 // eip options: START
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/enterprise-integration-patterns.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/enterprise-integration-patterns.adoc
index 819428ae457c..b20b1f8c243d 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/enterprise-integration-patterns.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/enterprise-integration-patterns.adoc
@@ -118,6 +118,10 @@ a|image::eip/ContentBasedRouterIcon.gif[image]
 a situation where the implementation of a single logical function (e.g.,
 inventory check) is spread across multiple physical systems?
 
+a|image::eip/ContentBasedRouterIcon.gif[image]
+|xref:switch-eip.adoc[Switch] |How do we route a message to a fixed endpoint by
+the literal value of a single selector, using a decision table?
+
 a|image::eip/MessageFilterIcon.gif[image]
 |xref:filter-eip.adoc[Message Filter] |How can a component avoid
 receiving uninteresting messages?
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/switch-eip.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/switch-eip.adoc
index 79b1b441406f..4fab8169deb6 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/switch-eip.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/switch-eip.adoc
@@ -17,6 +17,11 @@ predicates, ranges, overlapping conditions, or nested 
processing steps. A case c
 send to a `direct:` route when more processing is needed. Use 
xref:toD-eip.adoc[To D]
 when the endpoint URI itself must be calculated dynamically.
 
+TIP: Switch has no inline processing steps: each case sends to one endpoint.
+Keep each branch in its own route, linked through 
xref:ROOT:direct-component.adoc[Direct]
+or xref:ROOT:seda-component.adoc[SEDA], so it can be read and tested 
separately.
+For predicates, ranges, or inline processing steps, use 
xref:choice-eip.adoc[Choice].
+
 == Options
 
 // eip options: START
@@ -36,8 +41,9 @@ They follow normal Camel error handling and do not select 
`otherwise`.
 === Route from a header
 
 This route reads the `department` header. The body is passed unchanged to the
-selected destination. The examples on this page are independent; load one at a
-time and connect the `direct:` destinations to your application's handling 
routes.
+selected destination. Each case has a separate `direct:` handling route. 
Replace
+the logging steps with the application's processing. The examples on this page
+are independent; load one at a time.
 
 [tabs]
 ====
@@ -51,20 +57,29 @@ from("direct:tickets")
         .doCase("technical", "direct:technical")
         .otherwise("direct:review")
     .end();
+
+from("direct:billing").log("Billing: ${body}");
+from("direct:technical").log("Technical: ${body}");
+from("direct:review").log("Review: ${body}");
 ----
 XML::
 +
 [source,xml]
 ----
-<route xmlns="http://camel.apache.org/schema/spring";>
-    <from uri="direct:tickets"/>
-    <switch>
-        <selector><header>department</header></selector>
-        <case value="billing" uri="direct:billing"/>
-        <case value="technical" uri="direct:technical"/>
-        <otherwise uri="direct:review"/>
-    </switch>
-</route>
+<routes xmlns="http://camel.apache.org/schema/xml-io";>
+    <route>
+        <from uri="direct:tickets"/>
+        <switch>
+            <selector><header>department</header></selector>
+            <case value="billing" uri="direct:billing"/>
+            <case value="technical" uri="direct:technical"/>
+            <otherwise uri="direct:review"/>
+        </switch>
+    </route>
+    <route><from uri="direct:billing"/><log message="Billing: 
${body}"/></route>
+    <route><from uri="direct:technical"/><log message="Technical: 
${body}"/></route>
+    <route><from uri="direct:review"/><log message="Review: ${body}"/></route>
+</routes>
 ----
 YAML::
 +
@@ -85,6 +100,22 @@ YAML::
                 uri: direct:technical
             otherwise:
               uri: direct:review
+
+- route:
+    from:
+      uri: direct:billing
+      steps:
+        - log: "Billing: ${body}"
+- route:
+    from:
+      uri: direct:technical
+      steps:
+        - log: "Technical: ${body}"
+- route:
+    from:
+      uri: direct:review
+      steps:
+        - log: "Review: ${body}"
 ----
 ====
 
@@ -110,53 +141,59 @@ never select the fallback. Streams are reset after 
selector evaluation when stre
 caching is enabled. Loops and retries that enter Switch again evaluate the 
selector
 again; results are not cached across entries.
 
-=== Connect the example destinations
+=== Use the Endpoint DSL
 
-For a local demonstration, the destination routes can simply log the message.
-Load these routes alongside one of the examples above; replace the logging 
steps
-with the application's processing when integrating the example.
+With an xref:manual::Endpoint-dsl.adoc[EndpointRouteBuilder], cases and the 
fallback
+also accept endpoint builders. Both case forms support endpoint options, 
including
+object-valued options, just like `to(...)`:
 
-[tabs]
-====
-Java::
-+
 [source,java]
 ----
-from("direct:billing").log("Billing: ${body}");
-from("direct:technical").log("Technical: ${body}");
-from("direct:review").log("Review: ${body}");
-----
-XML::
-+
-[source,xml]
-----
-<routes xmlns="http://camel.apache.org/schema/xml-io";>
-    <route><from uri="direct:billing"/><log message="Billing: 
${body}"/></route>
-    <route><from uri="direct:technical"/><log message="Technical: 
${body}"/></route>
-    <route><from uri="direct:review"/><log message="Review: ${body}"/></route>
-</routes>
+from(direct("tickets"))
+    .doSwitch(header("department"))
+        .doCase("billing", direct("billing"))
+        .doCase("technical").to(direct("technical"))
+        .otherwise(direct("review"))
+    .end();
 ----
-YAML::
-+
+
+=== Classify and route with Semantic
+
+A xref:languages:semantic-language.adoc[Semantic] choice question returns a 
category
+string that Switch can match against its literal cases. Configure 
`camel-semantic`
+and a provider as described in the language documentation. This example uses 
one
+reference, `ref:department`, to evaluate the message body once and route the 
result.
+Load it alongside the `direct:billing`, `direct:technical`, and `direct:review`
+handling routes shown above.
+
 [source,yaml]
 ----
+- semantic:
+    question:
+      department:
+        type: choice
+        instructions: Which department should handle this message?
+        criteria:
+          billing: Invoices, payments and refunds
+          technical: Bugs, outages and technical problems
+          other: Any other request
 - route:
     from:
-      uri: direct:billing
-      steps:
-        - log: "Billing: ${body}"
-- route:
-    from:
-      uri: direct:technical
+      uri: direct:classify
       steps:
-        - log: "Technical: ${body}"
-- route:
-    from:
-      uri: direct:review
-      steps:
-        - log: "Review: ${body}"
+        - switch:
+            selector:
+              language:
+                language: semantic
+                expression: ref:department
+            case:
+              - value: billing
+                uri: direct:billing
+              - value: technical
+                uri: direct:technical
+            otherwise:
+              uri: direct:review
 ----
-====
 
 == Destinations and management
 
@@ -177,6 +214,10 @@ otherwise:
     name: review
 ----
 
+The `route-structure` developer console shows the destination of each case, 
such
+as `case[billing -> direct:billing]`, and identifies the fallback as
+`otherwise[direct:review]`. Sensitive URI parameters are masked in these 
labels.
+
 Each case has an identity for tracing, debugging and management. The Switch 
MBean's
 `extendedInformation` table reports case IDs, literal values, destination URIs 
and
 selection counts. URIs are masked when management masking is enabled (the 
default).
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/switch.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/switch.json
index 1da2d69acf5b..4befb27147a7 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/switch.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/switch.json
@@ -8,6 +8,7 @@
     "firstVersion": "4.23.0",
     "label": "eip,routing",
     "javaType": "org.apache.camel.model.SwitchDefinition",
+    "aliases": [ "decision-table", "dispatch-table", "lookup-table", "case" ],
     "abstract": false,
     "input": true,
     "output": false
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 320e9d67ea9f..eacf7a4d52cd 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
@@ -8369,6 +8369,7 @@
       "title" : "Switch",
       "description" : "Evaluates a selector once and dispatches to a fixed 
endpoint by literal scalar values",
       "label" : "eip,routing",
+      "aliases" : [ "decision-table", "dispatch-table", "lookup-table", "case" 
],
       "input" : true,
       "isListItem" : true,
       "children" : [ {
diff --git 
a/components/camel-spring-parent/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringSwitchTest.java
 
b/components/camel-spring-parent/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringSwitchTest.java
new file mode 100644
index 000000000000..93a3cd2a4518
--- /dev/null
+++ 
b/components/camel-spring-parent/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringSwitchTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.spring.processor;
+
+import org.apache.camel.spring.SpringTestSupport;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class SpringSwitchTest extends SpringTestSupport {
+    @Test
+    public void caseAndFallbackUseSeparateRoutes() throws Exception {
+        getMockEndpoint("mock:billing").expectedBodiesReceived("invoice");
+        getMockEndpoint("mock:technical").expectedBodiesReceived("outage");
+        getMockEndpoint("mock:review").expectedBodiesReceived("unknown", 
"missing");
+        getMockEndpoint("mock:after").expectedMessageCount(4);
+        template.sendBodyAndHeader("direct:start", "invoice", "department", 
"BILLING");
+        template.sendBodyAndHeader("direct:start", "outage", "department", 
"technical");
+        template.sendBodyAndHeader("direct:start", "unknown", "department", 
"other");
+        template.sendBody("direct:start", "missing");
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void xpathSelectorUsesSpringNamespaces() throws Exception {
+        String body = "<t:ticket 
xmlns:t='urn:tickets'><t:department>billing</t:department></t:ticket>";
+        getMockEndpoint("mock:billing").expectedBodiesReceived(body);
+        getMockEndpoint("mock:review").expectedMessageCount(0);
+        template.sendBody("direct:xpath", body);
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected AbstractXmlApplicationContext createApplicationContext() {
+        return new 
ClassPathXmlApplicationContext("org/apache/camel/spring/processor/SpringSwitchTest.xml");
+    }
+}
diff --git 
a/components/camel-spring-parent/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/SpringSwitchTest.xml
 
b/components/camel-spring-parent/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/SpringSwitchTest.xml
new file mode 100644
index 000000000000..20dc2edcf862
--- /dev/null
+++ 
b/components/camel-spring-parent/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/SpringSwitchTest.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns:t="urn:tickets"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans 
https://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://camel.apache.org/schema/spring 
https://camel.apache.org/schema/spring/camel-spring.xsd";>
+    <camelContext xmlns="http://camel.apache.org/schema/spring";>
+        <route id="dispatch">
+            <from uri="direct:start"/>
+            <switch id="byDepartment">
+                <selector><header>department</header></selector>
+                <case id="billingCase" value="billing" uri="direct:billing"/>
+                <case id="technicalCase" value="technical" 
uri="direct:technical"/>
+                <otherwise uri="direct:review"/>
+            </switch>
+            <to uri="mock:after"/>
+        </route>
+        <route id="xpathDispatch">
+            <from uri="direct:xpath"/>
+            <switch>
+                <selector><xpath 
resultType="java.lang.String">string(/t:ticket/t:department)</xpath></selector>
+                <case value="billing" uri="direct:billing"/>
+                <otherwise uri="direct:review"/>
+            </switch>
+        </route>
+        <route><from uri="direct:billing"/><to uri="mock:billing"/></route>
+        <route><from uri="direct:technical"/><to uri="mock:technical"/></route>
+        <route><from uri="direct:review"/><to uri="mock:review"/></route>
+    </camelContext>
+</beans>
diff --git 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/SwitchRouteStructureDevConsoleTest.java
 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/SwitchRouteStructureDevConsoleTest.java
new file mode 100644
index 000000000000..facbe152339f
--- /dev/null
+++ 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/SwitchRouteStructureDevConsoleTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl.console;
+
+import java.util.Map;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.model.SwitchDefinition;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class SwitchRouteStructureDevConsoleTest extends ContextTestSupport {
+    @ParameterizedTest
+    @ValueSource(booleans = { false, true })
+    void labelsIdentifyCaseAndFallbackDestinations(boolean brief) {
+        DevConsole console = 
PluginHelper.getDevConsoleResolver(context).resolveDevConsole("route-structure");
+        String text = (String) console.call(DevConsole.MediaType.TEXT, 
Map.of("filter", "tickets", "brief", brief));
+        assertTrue(text.contains("case[billing -> direct:billing]"), text);
+        assertTrue(text.contains("case[technical -> direct:technical]"), text);
+        assertTrue(text.contains("otherwise[direct:review]"), text);
+
+        JsonObject json = (JsonObject) console.call(DevConsole.MediaType.JSON, 
Map.of("filter", "tickets", "brief", brief));
+        JsonObject route = (JsonObject) 
json.getCollection("routes").iterator().next();
+        JsonObject fallback = 
route.getCollection("code").stream().map(JsonObject.class::cast)
+                .filter(line -> 
"dispatch-otherwise".equals(line.getString("id"))).findFirst().orElseThrow();
+        assertEquals("to", fallback.getString("type"));
+        assertEquals("otherwise[direct:review]", fallback.getString("code"));
+        assertEquals("direct:review", fallback.getString("uri"));
+    }
+
+    @Test
+    void labelsMaskSecretsAndBriefModeKeepsTheFallbackIdentity() {
+        SwitchDefinition sw = (SwitchDefinition) 
context.getRouteDefinition("tickets").getOutputs().get(0);
+        sw.getCases().get(0).setUri("direct:billing?password=caseSecret");
+        sw.getOtherwise().setUri("direct:review?password=fallbackSecret");
+        DevConsole console = 
PluginHelper.getDevConsoleResolver(context).resolveDevConsole("route-structure");
+        String text = (String) console.call(DevConsole.MediaType.TEXT, 
Map.of("filter", "tickets"));
+        assertTrue(text.contains("case[billing -> direct:billing?password="), 
text);
+        assertTrue(text.contains("otherwise[direct:review?password="), text);
+        assertFalse(text.contains("caseSecret"), text);
+        assertFalse(text.contains("fallbackSecret"), text);
+
+        String brief = (String) console.call(DevConsole.MediaType.TEXT, 
Map.of("filter", "tickets", "brief", true));
+        assertTrue(brief.contains("case[billing -> direct:billing]"), brief);
+        assertTrue(brief.contains("otherwise[direct:review]"), brief);
+        assertFalse(brief.contains("password"), brief);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:tickets").routeId("tickets")
+                        .doSwitch(header("department")).id("dispatch")
+                            .doCase("billing", "direct:billing")
+                            .doCase("technical").to("direct:technical")
+                            .otherwise("direct:review")
+                        .end();
+                from("direct:billing").to("mock:billing");
+                from("direct:technical").to("mock:technical");
+                from("direct:review").to("mock:review");
+            }
+        };
+    }
+}
diff --git 
a/core/camel-core-engine/src/main/docs/modules/eips/pages/ai-patterns.adoc 
b/core/camel-core-engine/src/main/docs/modules/eips/pages/ai-patterns.adoc
index a783c5882f69..d77c72900306 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/ai-patterns.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/ai-patterns.adoc
@@ -48,7 +48,7 @@ See also the full 
xref:enterprise-integration-patterns.adoc[Enterprise Integrati
 |Discard messages that do not match a predicate.
 
 |Router / Dispatch
-|xref:choice-eip.adoc[Content-Based Router], 
xref:dynamicRouter-eip.adoc[Dynamic Router]
+|xref:choice-eip.adoc[Content-Based Router], xref:switch-eip.adoc[Switch], 
xref:dynamicRouter-eip.adoc[Dynamic Router]
 |Route messages to different destinations based on content or rules.
 |===
 
@@ -208,6 +208,10 @@ See also the full 
xref:enterprise-integration-patterns.adoc[Enterprise Integrati
 |===
 |Term |Camel Component / EIP |Description
 
+|Classify / Intent routing
+|xref:languages:semantic-language.adoc[Semantic], xref:switch-eip.adoc[Switch]
+|Evaluate a Semantic choice question once and dispatch its category string 
through literal Switch cases.
+
 |Agent-to-Agent / A2A
 |xref:ROOT:a2a-component.adoc[A2A]
 |Google's Agent-to-Agent protocol for communication between AI agents.
diff --git 
a/core/camel-core-engine/src/main/docs/modules/eips/pages/choice-eip.adoc 
b/core/camel-core-engine/src/main/docs/modules/eips/pages/choice-eip.adoc
index 0e3d67c6ba1a..c9f0645698c2 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/choice-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/choice-eip.adoc
@@ -13,6 +13,12 @@ on the contents of the message exchanges.
 
 image::eip/ContentBasedRouter.gif[image]
 
+TIP: For a decision table based on the literal value of one expression, use
+xref:switch-eip.adoc[Switch]. Each Switch case sends to one endpoint, with no 
inline
+processing steps. Branches can be separate routes linked through
+xref:ROOT:direct-component.adoc[Direct] or xref:ROOT:seda-component.adoc[SEDA].
+Choice supports predicates, ranges, and inline processing steps.
+
 == Choice options
 
 // eip options: START
diff --git 
a/core/camel-core-engine/src/main/docs/modules/eips/pages/enterprise-integration-patterns.adoc
 
b/core/camel-core-engine/src/main/docs/modules/eips/pages/enterprise-integration-patterns.adoc
index 819428ae457c..b20b1f8c243d 100644
--- 
a/core/camel-core-engine/src/main/docs/modules/eips/pages/enterprise-integration-patterns.adoc
+++ 
b/core/camel-core-engine/src/main/docs/modules/eips/pages/enterprise-integration-patterns.adoc
@@ -118,6 +118,10 @@ a|image::eip/ContentBasedRouterIcon.gif[image]
 a situation where the implementation of a single logical function (e.g.,
 inventory check) is spread across multiple physical systems?
 
+a|image::eip/ContentBasedRouterIcon.gif[image]
+|xref:switch-eip.adoc[Switch] |How do we route a message to a fixed endpoint by
+the literal value of a single selector, using a decision table?
+
 a|image::eip/MessageFilterIcon.gif[image]
 |xref:filter-eip.adoc[Message Filter] |How can a component avoid
 receiving uninteresting messages?
diff --git 
a/core/camel-core-engine/src/main/docs/modules/eips/pages/switch-eip.adoc 
b/core/camel-core-engine/src/main/docs/modules/eips/pages/switch-eip.adoc
index 79b1b441406f..4fab8169deb6 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/switch-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/switch-eip.adoc
@@ -17,6 +17,11 @@ predicates, ranges, overlapping conditions, or nested 
processing steps. A case c
 send to a `direct:` route when more processing is needed. Use 
xref:toD-eip.adoc[To D]
 when the endpoint URI itself must be calculated dynamically.
 
+TIP: Switch has no inline processing steps: each case sends to one endpoint.
+Keep each branch in its own route, linked through 
xref:ROOT:direct-component.adoc[Direct]
+or xref:ROOT:seda-component.adoc[SEDA], so it can be read and tested 
separately.
+For predicates, ranges, or inline processing steps, use 
xref:choice-eip.adoc[Choice].
+
 == Options
 
 // eip options: START
@@ -36,8 +41,9 @@ They follow normal Camel error handling and do not select 
`otherwise`.
 === Route from a header
 
 This route reads the `department` header. The body is passed unchanged to the
-selected destination. The examples on this page are independent; load one at a
-time and connect the `direct:` destinations to your application's handling 
routes.
+selected destination. Each case has a separate `direct:` handling route. 
Replace
+the logging steps with the application's processing. The examples on this page
+are independent; load one at a time.
 
 [tabs]
 ====
@@ -51,20 +57,29 @@ from("direct:tickets")
         .doCase("technical", "direct:technical")
         .otherwise("direct:review")
     .end();
+
+from("direct:billing").log("Billing: ${body}");
+from("direct:technical").log("Technical: ${body}");
+from("direct:review").log("Review: ${body}");
 ----
 XML::
 +
 [source,xml]
 ----
-<route xmlns="http://camel.apache.org/schema/spring";>
-    <from uri="direct:tickets"/>
-    <switch>
-        <selector><header>department</header></selector>
-        <case value="billing" uri="direct:billing"/>
-        <case value="technical" uri="direct:technical"/>
-        <otherwise uri="direct:review"/>
-    </switch>
-</route>
+<routes xmlns="http://camel.apache.org/schema/xml-io";>
+    <route>
+        <from uri="direct:tickets"/>
+        <switch>
+            <selector><header>department</header></selector>
+            <case value="billing" uri="direct:billing"/>
+            <case value="technical" uri="direct:technical"/>
+            <otherwise uri="direct:review"/>
+        </switch>
+    </route>
+    <route><from uri="direct:billing"/><log message="Billing: 
${body}"/></route>
+    <route><from uri="direct:technical"/><log message="Technical: 
${body}"/></route>
+    <route><from uri="direct:review"/><log message="Review: ${body}"/></route>
+</routes>
 ----
 YAML::
 +
@@ -85,6 +100,22 @@ YAML::
                 uri: direct:technical
             otherwise:
               uri: direct:review
+
+- route:
+    from:
+      uri: direct:billing
+      steps:
+        - log: "Billing: ${body}"
+- route:
+    from:
+      uri: direct:technical
+      steps:
+        - log: "Technical: ${body}"
+- route:
+    from:
+      uri: direct:review
+      steps:
+        - log: "Review: ${body}"
 ----
 ====
 
@@ -110,53 +141,59 @@ never select the fallback. Streams are reset after 
selector evaluation when stre
 caching is enabled. Loops and retries that enter Switch again evaluate the 
selector
 again; results are not cached across entries.
 
-=== Connect the example destinations
+=== Use the Endpoint DSL
 
-For a local demonstration, the destination routes can simply log the message.
-Load these routes alongside one of the examples above; replace the logging 
steps
-with the application's processing when integrating the example.
+With an xref:manual::Endpoint-dsl.adoc[EndpointRouteBuilder], cases and the 
fallback
+also accept endpoint builders. Both case forms support endpoint options, 
including
+object-valued options, just like `to(...)`:
 
-[tabs]
-====
-Java::
-+
 [source,java]
 ----
-from("direct:billing").log("Billing: ${body}");
-from("direct:technical").log("Technical: ${body}");
-from("direct:review").log("Review: ${body}");
-----
-XML::
-+
-[source,xml]
-----
-<routes xmlns="http://camel.apache.org/schema/xml-io";>
-    <route><from uri="direct:billing"/><log message="Billing: 
${body}"/></route>
-    <route><from uri="direct:technical"/><log message="Technical: 
${body}"/></route>
-    <route><from uri="direct:review"/><log message="Review: ${body}"/></route>
-</routes>
+from(direct("tickets"))
+    .doSwitch(header("department"))
+        .doCase("billing", direct("billing"))
+        .doCase("technical").to(direct("technical"))
+        .otherwise(direct("review"))
+    .end();
 ----
-YAML::
-+
+
+=== Classify and route with Semantic
+
+A xref:languages:semantic-language.adoc[Semantic] choice question returns a 
category
+string that Switch can match against its literal cases. Configure 
`camel-semantic`
+and a provider as described in the language documentation. This example uses 
one
+reference, `ref:department`, to evaluate the message body once and route the 
result.
+Load it alongside the `direct:billing`, `direct:technical`, and `direct:review`
+handling routes shown above.
+
 [source,yaml]
 ----
+- semantic:
+    question:
+      department:
+        type: choice
+        instructions: Which department should handle this message?
+        criteria:
+          billing: Invoices, payments and refunds
+          technical: Bugs, outages and technical problems
+          other: Any other request
 - route:
     from:
-      uri: direct:billing
-      steps:
-        - log: "Billing: ${body}"
-- route:
-    from:
-      uri: direct:technical
+      uri: direct:classify
       steps:
-        - log: "Technical: ${body}"
-- route:
-    from:
-      uri: direct:review
-      steps:
-        - log: "Review: ${body}"
+        - switch:
+            selector:
+              language:
+                language: semantic
+                expression: ref:department
+            case:
+              - value: billing
+                uri: direct:billing
+              - value: technical
+                uri: direct:technical
+            otherwise:
+              uri: direct:review
 ----
-====
 
 == Destinations and management
 
@@ -177,6 +214,10 @@ otherwise:
     name: review
 ----
 
+The `route-structure` developer console shows the destination of each case, 
such
+as `case[billing -> direct:billing]`, and identifies the fallback as
+`otherwise[direct:review]`. Sensitive URI parameters are masked in these 
labels.
+
 Each case has an identity for tracing, debugging and management. The Switch 
MBean's
 `extendedInformation` table reports case IDs, literal values, destination URIs 
and
 selection counts. URIs are masked when management masking is enabled (the 
default).
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModelToStructureDumper.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModelToStructureDumper.java
index eca8d90ce6a3..be180734a76d 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModelToStructureDumper.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModelToStructureDumper.java
@@ -28,11 +28,14 @@ import org.apache.camel.model.EndpointRequiredDefinition;
 import org.apache.camel.model.Model;
 import org.apache.camel.model.OptionalIdentifiedDefinition;
 import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.SwitchCaseDefinition;
+import org.apache.camel.model.SwitchDefinition;
 import org.apache.camel.spi.ModelDumpLine;
 import org.apache.camel.spi.ModelToStructureDumper;
 import org.apache.camel.spi.annotations.JdkService;
 import org.apache.camel.support.LoggerHelper;
 import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.URISupport;
 
 @JdkService(ModelToStructureDumper.FACTORY)
 public class DefaultModelToStructureDumper implements ModelToStructureDumper {
@@ -87,9 +90,17 @@ public class DefaultModelToStructureDumper implements 
ModelToStructureDumper {
                     remote = isRemoteUri(endpointUri, schemeRemoteMap);
                     if (brief) {
                         String uri = StringHelper.before(endpointUri, "?", 
endpointUri);
-                        code = output.getShortName() + "[" + uri + "]";
+                        if (output instanceof SwitchCaseDefinition c) {
+                            code = "case[" + c.getValue() + " -> " + 
URISupport.sanitizeUri(uri) + "]";
+                        } else {
+                            code = output.getShortName() + "[" + uri + "]";
+                        }
                     }
                 }
+                if (parent instanceof SwitchDefinition sw && output == 
sw.getOtherwiseDefinition()) {
+                    String uri = URISupport.sanitizeUri(endpointUri);
+                    code = "otherwise[" + (brief ? StringHelper.before(uri, 
"?", uri) : uri) + "]";
+                }
                 answer.add(new ModelDumpLine(loc, kind, id, level, code, 
output.getDescription(), endpointUri, remote));
             }
             dumpChildren(child, scheme, brief, level + 1, answer, 
schemeRemoteMap);
diff --git 
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/switch.json
 
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/switch.json
index 1da2d69acf5b..4befb27147a7 100644
--- 
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/switch.json
+++ 
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/switch.json
@@ -8,6 +8,7 @@
     "firstVersion": "4.23.0",
     "label": "eip,routing",
     "javaType": "org.apache.camel.model.SwitchDefinition",
+    "aliases": [ "decision-table", "dispatch-table", "lookup-table", "case" ],
     "abstract": false,
     "input": true,
     "output": false
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchCaseDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchCaseDefinition.java
index fbbabe0c9845..99de82bbc378 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchCaseDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchCaseDefinition.java
@@ -22,7 +22,9 @@ import jakarta.xml.bind.annotation.XmlAttribute;
 import jakarta.xml.bind.annotation.XmlRootElement;
 import jakarta.xml.bind.annotation.XmlTransient;
 
+import org.apache.camel.builder.EndpointProducerBuilder;
 import org.apache.camel.spi.Metadata;
+import org.apache.camel.util.URISupport;
 
 /** One literal value and one fixed destination. */
 @Metadata(label = "configuration", description = "A literal switch case with 
one fixed endpoint destination")
@@ -37,6 +39,8 @@ public class SwitchCaseDefinition extends 
OptionalIdentifiedDefinition<SwitchCas
     @Metadata(description = "The fixed destination URI. Supports property 
placeholders, but not Simple expressions.")
     private String uri;
     @XmlTransient
+    private EndpointProducerBuilder endpointProducerBuilder;
+    @XmlTransient
     private SwitchDefinition parent;
     @XmlTransient
     private ToDefinition toDefinition;
@@ -53,6 +57,7 @@ public class SwitchCaseDefinition extends 
OptionalIdentifiedDefinition<SwitchCas
         super(source);
         this.value = source.value;
         this.uri = source.uri;
+        this.endpointProducerBuilder = source.endpointProducerBuilder;
     }
 
     public SwitchCaseDefinition copyDefinition() {
@@ -68,10 +73,22 @@ public class SwitchCaseDefinition extends 
OptionalIdentifiedDefinition<SwitchCas
     }
 
     public String getUri() {
-        return uri;
+        return endpointProducerBuilder != null ? 
endpointProducerBuilder.getRawUri() : uri;
+    }
+
+    @XmlTransient
+    public EndpointProducerBuilder getEndpointProducerBuilder() {
+        return endpointProducerBuilder;
+    }
+
+    public void setEndpointProducerBuilder(EndpointProducerBuilder 
endpointProducerBuilder) {
+        this.endpointProducerBuilder = endpointProducerBuilder;
+        // JAXB reads this field; the dump helper refreshes it if the builder 
changes.
+        this.uri = endpointProducerBuilder != null ? 
endpointProducerBuilder.getRawUri() : null;
     }
 
     public void setUri(String uri) {
+        this.endpointProducerBuilder = null;
         this.uri = uri;
     }
 
@@ -86,7 +103,11 @@ public class SwitchCaseDefinition extends 
OptionalIdentifiedDefinition<SwitchCas
         if (toDefinition == null) {
             toDefinition = new ToDefinition();
         }
-        toDefinition.setUri(uri);
+        if (endpointProducerBuilder != null) {
+            toDefinition.setEndpointProducerBuilder(endpointProducerBuilder);
+        } else {
+            toDefinition.setUri(uri);
+        }
         toDefinition.setParent(parent);
         if (hasCustomIdAssigned()) {
             toDefinition.setId(getId());
@@ -101,7 +122,7 @@ public class SwitchCaseDefinition extends 
OptionalIdentifiedDefinition<SwitchCas
 
     @Override
     public String getEndpointUri() {
-        return uri;
+        return getUri();
     }
 
     @Override
@@ -120,11 +141,11 @@ public class SwitchCaseDefinition extends 
OptionalIdentifiedDefinition<SwitchCas
 
     @Override
     public String getLabel() {
-        return "case[" + value + "]";
+        return "case[" + value + " -> " + 
URISupport.sanitizeUri(getEndpointUri()) + "]";
     }
 
     @Override
     public String toString() {
-        return getLabel() + " -> " + uri;
+        return getLabel();
     }
 }
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchDefinition.java
index 944d6e059e50..65eff9dd77d5 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchDefinition.java
@@ -28,12 +28,15 @@ import jakarta.xml.bind.annotation.XmlType;
 
 import org.apache.camel.Expression;
 import org.apache.camel.NamedNode;
+import org.apache.camel.builder.EndpointProducerBuilder;
 import org.apache.camel.builder.ExpressionClause;
 import org.apache.camel.model.language.ExpressionDefinition;
+import org.apache.camel.spi.AsEndpointUri;
 import org.apache.camel.spi.Metadata;
 
 /** Routes a message to one fixed endpoint using a literal lookup table. */
 @Metadata(firstVersion = "4.23.0", label = "eip,routing",
+          aliases = { "decision-table", "dispatch-table", "lookup-table", 
"case" },
           description = "Evaluates a selector once and dispatches to a fixed 
endpoint by literal scalar values")
 @XmlRootElement(name = "switch")
 @XmlAccessorType(XmlAccessType.FIELD)
@@ -132,7 +135,11 @@ public class SwitchDefinition extends 
NoOutputDefinition<SwitchDefinition> {
             return;
         }
         otherwiseDefinition.setParent(this);
-        otherwiseDefinition.setUri(otherwise.getUri());
+        if (otherwise.getEndpointProducerBuilder() != null) {
+            
otherwiseDefinition.setEndpointProducerBuilder(otherwise.getEndpointProducerBuilder());
+        } else {
+            otherwiseDefinition.setUri(otherwise.getUri());
+        }
         if (getId() != null) {
             if (hasCustomIdAssigned()) {
                 otherwiseDefinition.setId(getId() + "-otherwise");
@@ -147,6 +154,14 @@ public class SwitchDefinition extends 
NoOutputDefinition<SwitchDefinition> {
         return doCase(new SwitchCaseDefinition(value, uri));
     }
 
+    /** Add a scalar literal and its destination using the Endpoint DSL. */
+    public SwitchDefinition doCase(String value, @AsEndpointUri 
EndpointProducerBuilder endpoint) {
+        SwitchCaseDefinition c = new SwitchCaseDefinition();
+        c.setValue(value);
+        c.setEndpointProducerBuilder(endpoint);
+        return doCase(c);
+    }
+
     /** Add a case definition. */
     public SwitchDefinition doCase(SwitchCaseDefinition value) {
         value.setParent(this);
@@ -175,6 +190,17 @@ public class SwitchDefinition extends 
NoOutputDefinition<SwitchDefinition> {
         return this;
     }
 
+    /** Set the fixed fallback destination using the Endpoint DSL. */
+    public SwitchDefinition otherwise(@AsEndpointUri EndpointProducerBuilder 
endpoint) {
+        SwitchOtherwiseDefinition fallback = null;
+        if (endpoint != null) {
+            fallback = new SwitchOtherwiseDefinition();
+            fallback.setEndpointProducerBuilder(endpoint);
+        }
+        setOtherwise(fallback);
+        return this;
+    }
+
     @Override
     public List<NamedNode> getChildren() {
         List<NamedNode> children = new ArrayList<>(cases);
@@ -225,6 +251,11 @@ public class SwitchDefinition extends 
NoOutputDefinition<SwitchDefinition> {
             return this;
         }
 
+        public SwitchDefinition to(@AsEndpointUri EndpointProducerBuilder 
endpoint) {
+            definition.setEndpointProducerBuilder(endpoint);
+            return parent.doCase(definition);
+        }
+
         public SwitchDefinition to(String uri) {
             definition.setUri(uri);
             return parent.doCase(definition);
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchOtherwiseDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchOtherwiseDefinition.java
index d5cd511b2089..4783a7e033c6 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchOtherwiseDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/SwitchOtherwiseDefinition.java
@@ -23,6 +23,7 @@ import jakarta.xml.bind.annotation.XmlAttribute;
 import jakarta.xml.bind.annotation.XmlRootElement;
 import jakarta.xml.bind.annotation.XmlTransient;
 
+import org.apache.camel.builder.EndpointProducerBuilder;
 import org.apache.camel.spi.Metadata;
 
 /** The fixed fallback destination of a switch. */
@@ -34,19 +35,38 @@ public class SwitchOtherwiseDefinition implements 
EndpointRequiredDefinition {
     @Metadata(description = "The fixed destination URI. Supports property 
placeholders, but not Simple expressions.")
     private String uri;
     @XmlTransient
+    private EndpointProducerBuilder endpointProducerBuilder;
+    @XmlTransient
     private final ToDefinition toDefinition = new ToDefinition();
 
     public SwitchOtherwiseDefinition copyDefinition() {
         SwitchOtherwiseDefinition copy = new SwitchOtherwiseDefinition();
-        copy.setUri(uri);
+        if (endpointProducerBuilder != null) {
+            copy.setEndpointProducerBuilder(endpointProducerBuilder);
+        } else {
+            copy.setUri(uri);
+        }
         return copy;
     }
 
     public String getUri() {
-        return uri;
+        return endpointProducerBuilder != null ? 
endpointProducerBuilder.getRawUri() : uri;
+    }
+
+    @XmlTransient
+    public EndpointProducerBuilder getEndpointProducerBuilder() {
+        return endpointProducerBuilder;
+    }
+
+    public void setEndpointProducerBuilder(EndpointProducerBuilder 
endpointProducerBuilder) {
+        this.endpointProducerBuilder = endpointProducerBuilder;
+        // JAXB reads this field; the dump helper refreshes it if the builder 
changes.
+        this.uri = endpointProducerBuilder != null ? 
endpointProducerBuilder.getRawUri() : null;
+        toDefinition.setEndpointProducerBuilder(endpointProducerBuilder);
     }
 
     public void setUri(String uri) {
+        this.endpointProducerBuilder = null;
         this.uri = uri;
         toDefinition.setUri(uri);
     }
@@ -68,6 +88,6 @@ public class SwitchOtherwiseDefinition implements 
EndpointRequiredDefinition {
     @Override
     @XmlTransient
     public String getEndpointUri() {
-        return uri;
+        return getUri();
     }
 }
diff --git 
a/core/camel-core-model/src/test/java/org/apache/camel/model/SwitchDefinitionTest.java
 
b/core/camel-core-model/src/test/java/org/apache/camel/model/SwitchDefinitionTest.java
index 715e1a31075d..451d30b5a215 100644
--- 
a/core/camel-core-model/src/test/java/org/apache/camel/model/SwitchDefinitionTest.java
+++ 
b/core/camel-core-model/src/test/java/org/apache/camel/model/SwitchDefinitionTest.java
@@ -19,9 +19,11 @@ package org.apache.camel.model;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotSame;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class SwitchDefinitionTest {
     @Test
@@ -52,6 +54,16 @@ class SwitchDefinitionTest {
         assertEquals("prepared-fallback", fallbackSend.getId());
     }
 
+    @Test
+    void caseLabelIncludesTheDestinationAndMasksSecrets() {
+        SwitchCaseDefinition c = new SwitchCaseDefinition("billing", 
"https://example.com?password=secret";);
+        assertTrue(c.getLabel().startsWith("case[billing -> 
https://example.com?password=";));
+        assertFalse(c.getLabel().contains("secret"));
+        assertEquals(c.getLabel(), c.toString());
+        c.setUri("direct:updated");
+        assertEquals("case[billing -> direct:updated]", c.getLabel());
+    }
+
     @Test
     void copiedFallbackHasIndependentDestinationAndParent() {
         SwitchDefinition original = new 
SwitchDefinition().otherwise("mock:original");
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SwitchReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SwitchReifier.java
index 77adf6751f4c..44ccda5e85e7 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SwitchReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SwitchReifier.java
@@ -69,7 +69,9 @@ public class SwitchReifier extends 
ProcessorReifier<SwitchDefinition> {
             c.idOrCreate(ids);
             c.prepareToDefinition();
             ToDefinition send = c.getToDefinition();
-            send.setUri(uris.get(i));
+            if (c.getEndpointProducerBuilder() == null) {
+                send.setUri(uris.get(i));
+            }
             cases.put(caseKeys.get(i), createSend(send));
         }
         Processor otherwise = null;
@@ -77,7 +79,9 @@ public class SwitchReifier extends 
ProcessorReifier<SwitchDefinition> {
             definition.idOrCreate(ids);
             definition.prepareOtherwiseDefinition();
             ToDefinition send = definition.getOtherwiseDefinition();
-            send.setUri(otherwiseUri);
+            if (definition.getOtherwise().getEndpointProducerBuilder() == 
null) {
+                send.setUri(otherwiseUri);
+            }
             otherwise = createSend(send);
         }
         SwitchProcessor answer = new SwitchProcessor(camelContext, selector, 
cases, otherwise);
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/processor/SwitchValidationTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/processor/SwitchValidationTest.java
index 7c6eca2798dd..962d45f43973 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/processor/SwitchValidationTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/processor/SwitchValidationTest.java
@@ -43,7 +43,7 @@ class SwitchValidationTest {
 
     @Test
     void rejectsMissingUri() {
-        rejects(s -> s.doCase("billing", null), "nonblank uri");
+        rejects(s -> s.doCase("billing", (String) null), "nonblank uri");
     }
 
     @Test
diff --git 
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
 
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
index d582aeb265f6..a83302815478 100644
--- 
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
+++ 
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
@@ -441,6 +441,35 @@ class LwJavaParserTest {
         
assertThat(java).contains("routingSlip(constant(\"mock://m2,direct://c\"))", 
"com.acme.Errors$Invalid");
     }
 
+    @Test
+    void switchDestinationsAcceptEndpointDslSyntax() {
+        JavaParseResult result = new LwJavaParser().parse("""
+                import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
+
+                public class Tickets extends EndpointRouteBuilder {
+                    public void configure() {
+                        from(direct("tickets")).routeId("tickets")
+                            .doSwitch(header("department"))
+                                .doCase("billing", direct("billing"))
+                                
.doCase("technical").id("technicalCase").to(direct("technical"))
+                                .otherwise(direct("review"))
+                            .end()
+                            .to(mock("after"));
+                    }
+                }
+                """);
+        assertThat(result.isComplete()).as("%s", result.unresolved()).isTrue();
+        RouteDefinition route = result.routes().getRoutes().get(0);
+        assertThat(route.getInput().getUri()).isEqualTo("direct://tickets");
+        SwitchDefinition sw = (SwitchDefinition) route.getOutputs().get(0);
+        
assertThat(sw.getCases()).extracting(SwitchCaseDefinition::getValue).containsExactly("billing",
 "technical");
+        assertThat(sw.getCases()).extracting(SwitchCaseDefinition::getUri)
+                .containsExactly("direct://billing", "direct://technical");
+        assertThat(sw.getCases().get(1).getId()).isEqualTo("technicalCase");
+        assertThat(sw.getOtherwise().getUri()).isEqualTo("direct://review");
+        assertThat(((ToDefinition) 
route.getOutputs().get(1)).getUri()).isEqualTo("mock://after");
+    }
+
     @Test
     void classNamesFormatsAndArithmetic() {
         JavaParseResult result = new LwJavaParser().parse("""
diff --git a/core/camel-xml-io/src/test/resources/switch.xml 
b/core/camel-xml-io/src/test/resources/switch.xml
new file mode 100644
index 000000000000..73b2b016c94d
--- /dev/null
+++ b/core/camel-xml-io/src/test/resources/switch.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<routes xmlns="http://camel.apache.org/schema/xml-io"; xmlns:t="urn:tickets">
+    <route id="headerSwitch">
+        <from uri="direct:header"/>
+        <switch id="byDepartment">
+            <selector><header>department</header></selector>
+            <case id="billingCase" value="billing" uri="direct:billing"/>
+            <case id="technicalCase" value="technical" uri="direct:technical"/>
+            <case id="numericCase" value="001" uri="direct:numeric"/>
+            <otherwise uri="direct:review"/>
+        </switch>
+        <to uri="mock:after"/>
+    </route>
+    <route id="xpathSwitch">
+        <from uri="direct:xpath"/>
+        <switch id="byXPath">
+            <selector><xpath 
resultType="java.lang.String">string(/t:ticket/t:department)</xpath></selector>
+            <case id="xpathBillingCase" value="billing" uri="direct:billing"/>
+            <otherwise uri="direct:review"/>
+        </switch>
+    </route>
+</routes>
diff --git 
a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbHelper.java 
b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbHelper.java
index 62c746934a2f..ac1a081a7565 100644
--- 
a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbHelper.java
+++ 
b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbHelper.java
@@ -56,7 +56,9 @@ import org.apache.camel.model.RouteTemplateDefinition;
 import org.apache.camel.model.RouteTemplatesDefinition;
 import org.apache.camel.model.RoutesDefinition;
 import org.apache.camel.model.SendDefinition;
+import org.apache.camel.model.SwitchCaseDefinition;
 import org.apache.camel.model.SwitchDefinition;
+import org.apache.camel.model.SwitchOtherwiseDefinition;
 import org.apache.camel.model.TemplatedRouteDefinition;
 import org.apache.camel.model.TemplatedRoutesDefinition;
 import org.apache.camel.model.ToDynamicDefinition;
@@ -170,6 +172,21 @@ public final class JaxbHelper {
                 restorers.add(() -> to.setUri(null));
             }
         }
+        for (SwitchDefinition sw : filterTypeInOutputs(route.getOutputs(), 
SwitchDefinition.class)) {
+            for (SwitchCaseDefinition c : sw.getCases()) {
+                EndpointProducerBuilder builder = 
c.getEndpointProducerBuilder();
+                if (builder != null) {
+                    c.setUri(builder.getRawUri());
+                    restorers.add(() -> c.setEndpointProducerBuilder(builder));
+                }
+            }
+            SwitchOtherwiseDefinition fallback = sw.getOtherwise();
+            if (fallback != null && fallback.getEndpointProducerBuilder() != 
null) {
+                EndpointProducerBuilder builder = 
fallback.getEndpointProducerBuilder();
+                fallback.setUri(builder.getRawUri());
+                restorers.add(() -> 
fallback.setEndpointProducerBuilder(builder));
+            }
+        }
         return () -> restorers.forEach(Runnable::run);
     }
 
diff --git 
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/io/YamlPrinterRoundTripTest.java
 
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/io/YamlPrinterRoundTripTest.java
index 0290567b8c6d..45df27b02b94 100644
--- 
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/io/YamlPrinterRoundTripTest.java
+++ 
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/io/YamlPrinterRoundTripTest.java
@@ -21,7 +21,10 @@ import java.util.Map;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.SwitchDefinition;
 import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.yaml.out.YamlModelWriter;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
@@ -29,6 +32,24 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class YamlPrinterRoundTripTest {
 
+    @ParameterizedTest
+    @ValueSource(strings = { "001", "true", "null", "", "*", "otherwise", 
"1e3" })
+    public void testSwitchLiteralValue(String value) throws Exception {
+        RouteDefinition route = new RouteDefinition("direct:start");
+        SwitchDefinition sw = route.doSwitch().header("department");
+        sw.doCase(value, "direct:matched").otherwise("direct:review");
+        sw.preCreateProcessor();
+        YamlModelWriter writer = new YamlModelWriter();
+
+        String yaml = 
writer.printAsYaml(List.of(writer.writeRouteDefinition(route)));
+        JsonNode node = new 
YAMLMapper().readTree(yaml).get(0).path("route").path("from").path("steps").get(0).path("switch");
+        JsonNode literal = node.path("case").get(0).path("value");
+        assertThat(literal.isTextual()).as(yaml).isTrue();
+        assertThat(literal.asText()).isEqualTo(value);
+        
assertThat(node.path("selector").path("header").path("expression").asText()).isEqualTo("department");
+        
assertThat(node.path("otherwise").path("uri").asText()).isEqualTo("direct:review");
+    }
+
     @ParameterizedTest
     @ValueSource(strings = {
             "Processing:", ",leading comma", "]x", "}x", "a\rb", " x\ny", "  
lead\nnext", "x\n\n", "007", "+5", "1.",
diff --git 
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/XmlToYamlTest.java 
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/XmlToYamlTest.java
index 20ce09adc10a..6228f6746dbb 100644
--- 
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/XmlToYamlTest.java
+++ 
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/XmlToYamlTest.java
@@ -26,19 +26,24 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Stream;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RoutesDefinition;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.xml.in.ModelParser;
 import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class XmlToYamlTest {
 
@@ -66,6 +71,32 @@ class XmlToYamlTest {
         }
     }
 
+    @Test
+    void switchPreservesSelectorNamespacesAndCaseDetails() throws Exception {
+        try (InputStream is = new 
FileInputStream("../camel-xml-io/src/test/resources/switch.xml")) {
+            RoutesDefinition routes = new ModelParser(is, 
NAMESPACE).parseRoutesDefinition().get();
+            YamlModelWriter writer = new YamlModelWriter();
+            List<JsonObject> roots = new ArrayList<>();
+            for (RouteDefinition route : routes.getRoutes()) {
+                roots.add(writer.writeRouteDefinition(route));
+            }
+            JsonNode yaml = new 
YAMLMapper().readTree(writer.printAsYaml(roots));
+            JsonNode sw = 
yaml.get(0).path("route").path("from").path("steps").get(0).path("switch");
+            assertEquals("department", 
sw.path("selector").path("header").path("expression").asText());
+            assertEquals("billingCase", 
sw.path("case").get(0).path("id").asText());
+            assertEquals("direct:billing", 
sw.path("case").get(0).path("uri").asText());
+            assertTrue(sw.path("case").get(2).path("value").isTextual());
+            assertEquals("001", sw.path("case").get(2).path("value").asText());
+            assertEquals("direct:review", 
sw.path("otherwise").path("uri").asText());
+            JsonNode xpath = 
yaml.get(1).path("route").path("from").path("steps").get(0)
+                    .path("switch").path("selector").path("xpath");
+            assertEquals("string(/t:ticket/t:department)", 
xpath.path("expression").asText());
+            assertEquals("java.lang.String", 
xpath.path("resultType").asText());
+            assertEquals("urn:tickets", 
xpath.path("namespace").get(0).path("value").asText());
+            assertEquals("t", 
xpath.path("namespace").get(0).path("key").asText());
+        }
+    }
+
     private static Stream<Arguments> routes() {
         return definitions("routes");
     }
diff --git 
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlModelWriterTest.java
 
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlModelWriterTest.java
index d974171434b3..9b34b518b958 100644
--- 
a/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlModelWriterTest.java
+++ 
b/core/camel-yaml-io/src/test/java/org/apache/camel/yaml/out/YamlModelWriterTest.java
@@ -75,6 +75,7 @@ import org.apache.camel.model.SortDefinition;
 import org.apache.camel.model.SplitDefinition;
 import org.apache.camel.model.StepDefinition;
 import org.apache.camel.model.StopDefinition;
+import org.apache.camel.model.SwitchDefinition;
 import org.apache.camel.model.ThreadsDefinition;
 import org.apache.camel.model.ThrottleDefinition;
 import org.apache.camel.model.ThrowExceptionDefinition;
@@ -157,6 +158,21 @@ public class YamlModelWriterTest {
         Assertions.assertEquals(expected, out);
     }
 
+    @Test
+    public void testSwitch() throws Exception {
+        RouteDefinition route = new 
RouteDefinition("direct:start").routeId("myRoute");
+        SwitchDefinition sw = route.doSwitch().header("department");
+        sw.doCase("billing", "direct:billing").doCase("001", 
"direct:numeric").otherwise("direct:review");
+        sw.getCases().get(0).setId("billingCase");
+        sw.preCreateProcessor();
+        route.to("mock:after");
+
+        YamlModelWriter writer = new YamlModelWriter();
+        String out = 
writer.printAsYaml(List.of(writer.writeRouteDefinition(route)));
+        String expected = 
stripLineComments(Paths.get("src/test/resources/yaml-route-switch.yaml"), "#", 
true);
+        Assertions.assertEquals(expected, out);
+    }
+
     @Test
     public void testChoice() throws Exception {
         YamlModelWriter writer = new YamlModelWriter();
diff --git a/core/camel-yaml-io/src/test/resources/yaml-route-switch.yaml 
b/core/camel-yaml-io/src/test/resources/yaml-route-switch.yaml
new file mode 100644
index 000000000000..612377add949
--- /dev/null
+++ b/core/camel-yaml-io/src/test/resources/yaml-route-switch.yaml
@@ -0,0 +1,38 @@
+#
+# 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:
+    customId: true
+    id: myRoute
+    from:
+      uri: direct:start
+      steps:
+        - switch:
+            selector:
+              header:
+                expression: department
+            case:
+              - customId: true
+                id: billingCase
+                value: billing
+                uri: direct:billing
+              - value: "001"
+                uri: direct:numeric
+            otherwise:
+              uri: direct:review
+        - to:
+            uri: mock:after
diff --git 
a/dsl/camel-endpointdsl/src/test/java/org/apache/camel/builder/endpoint/SwitchEndpointDslTest.java
 
b/dsl/camel-endpointdsl/src/test/java/org/apache/camel/builder/endpoint/SwitchEndpointDslTest.java
new file mode 100644
index 000000000000..057ec3e308cd
--- /dev/null
+++ 
b/dsl/camel-endpointdsl/src/test/java/org/apache/camel/builder/endpoint/SwitchEndpointDslTest.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.builder.endpoint;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.SwitchDefinition;
+import org.apache.camel.xml.jaxb.JaxbHelper;
+import org.apache.camel.xml.jaxb.JaxbModelToXMLDumper;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SwitchEndpointDslTest extends BaseEndpointDslTest {
+    private final BlockingQueue<Exchange> billing = new 
LinkedBlockingQueue<>();
+    private final BlockingQueue<Exchange> technical = new 
LinkedBlockingQueue<>();
+    private final BlockingQueue<Exchange> review = new LinkedBlockingQueue<>();
+
+    @Override
+    public boolean isUseAdviceWith() {
+        return true;
+    }
+
+    @Test
+    public void buildersSurviveCopyAndJaxbDumpAndResolveObjectOptions() throws 
Exception {
+        RouteDefinition route = context.getRouteDefinition("tickets");
+        SwitchDefinition sw = (SwitchDefinition) route.getOutputs().get(0);
+        SwitchDefinition copy = sw.copyDefinition();
+        assertNotSame(sw.getCases().get(0), copy.getCases().get(0));
+        assertNotSame(sw.getOtherwiseDefinition(), 
copy.getOtherwiseDefinition());
+        assertSame(sw.getCases().get(0).getEndpointProducerBuilder(), 
copy.getCases().get(0).getEndpointProducerBuilder());
+        assertSame(sw.getCases().get(1).getEndpointProducerBuilder(), 
copy.getCases().get(1).getEndpointProducerBuilder());
+        assertSame(sw.getOtherwise().getEndpointProducerBuilder(), 
copy.getOtherwise().getEndpointProducerBuilder());
+        copy.getCases().get(0).setUri("mock:copy");
+        copy.getOtherwise().setUri("mock:copyFallback");
+        assertNull(copy.getCases().get(0).getEndpointProducerBuilder());
+        assertNull(copy.getOtherwise().getEndpointProducerBuilder());
+
+        // Options added after the Switch was configured must also be visible 
to JAXB.
+        for (var c : sw.getCases()) {
+            c.getEndpointProducerBuilder().doSetProperty("blockWhenFull", 
true);
+        }
+        
sw.getOtherwise().getEndpointProducerBuilder().doSetProperty("blockWhenFull", 
true);
+        String xml = new JaxbModelToXMLDumper().dumpModelAsXml(context, route);
+        try (var input = new 
ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+            RouteDefinition restored = 
JaxbHelper.loadRoutesDefinition(context, input).getRoutes().get(0);
+            SwitchDefinition dumped = (SwitchDefinition) 
restored.getOutputs().get(0);
+            
assertTrue(dumped.getCases().get(0).getUri().contains("blockWhenFull=true"));
+            
assertTrue(dumped.getCases().get(1).getUri().contains("blockWhenFull=true"));
+            
assertTrue(dumped.getOtherwise().getUri().contains("blockWhenFull=true"));
+        }
+        assertEquals("seda://billing", 
sw.getCases().get(0).getUri().split("\\?")[0]);
+        context.start();
+        getMockEndpoint("mock:billing").expectedBodiesReceived("invoice");
+        getMockEndpoint("mock:technical").expectedBodiesReceived("outage");
+        getMockEndpoint("mock:review").expectedBodiesReceived("other", 
"missing");
+        template.sendBodyAndHeader("direct:tickets", "invoice", "department", 
"BILLING");
+        template.sendBodyAndHeader("direct:tickets", "outage", "department", 
"technical");
+        template.sendBodyAndHeader("direct:tickets", "other", "department", 
"other");
+        template.sendBody("direct:tickets", "missing");
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new EndpointRouteBuilder() {
+            @Override
+            public void configure() {
+                from(direct("tickets")).routeId("tickets")
+                        .doSwitch(header("department"))
+                            .doCase("billing", 
seda("billing").advanced().queue(billing))
+                            
.doCase("technical").to(seda("technical").advanced().queue(technical))
+                            .otherwise(seda("review").advanced().queue(review))
+                        .end();
+                
from(seda("billing").advanced().queue(billing)).to(mock("billing"));
+                
from(seda("technical").advanced().queue(technical)).to(mock("technical"));
+                
from(seda("review").advanced().queue(review)).to(mock("review"));
+            }
+        };
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
 
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
index 583da9fce285..71af218d0aae 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
@@ -1406,11 +1406,11 @@
   "switch": [
     {
       "source": "switch-eip.adoc",
-      "yaml": "- route:\n    from:\n      uri: direct:tickets\n      steps:\n  
      - switch:\n            selector:\n              header:\n                
expression: department\n            case:\n              - value: billing\n     
           uri: direct:billing\n              - value: technical\n              
  uri: direct:technical\n            otherwise:\n              uri: 
direct:review\n"
+      "yaml": "- route:\n    from:\n      uri: direct:tickets\n      steps:\n  
      - switch:\n            selector:\n              header:\n                
expression: department\n            case:\n              - value: billing\n     
           uri: direct:billing\n              - value: technical\n              
  uri: direct:technical\n            otherwise:\n              uri: 
direct:review\n\n- route:\n    from:\n      uri: direct:billing\n      steps:\n 
       - log: \"Billing: $ [...]
     },
     {
       "source": "switch-eip.adoc",
-      "yaml": "- route:\n    from:\n      uri: direct:billing\n      steps:\n  
      - log: \"Billing: ${body}\"\n- route:\n    from:\n      uri: 
direct:technical\n      steps:\n        - log: \"Technical: ${body}\"\n- 
route:\n    from:\n      uri: direct:review\n      steps:\n        - log: 
\"Review: ${body}\"\n"
+      "yaml": "- semantic:\n    question:\n      department:\n        type: 
choice\n        instructions: Which department should handle this message?\n    
    criteria:\n          billing: Invoices, payments and refunds\n          
technical: Bugs, outages and technical problems\n          other: Any other 
request\n- route:\n    from:\n      uri: direct:classify\n      steps:\n        
- switch:\n            selector:\n              language:\n                
language: semantic\n          [...]
     }
   ],
   "threads": [
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
index baeaffe09c3d..bd1b71e6e402 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
@@ -81,6 +81,8 @@ public final class CatalogSamples {
             Map.entry("onFallback", "circuitBreaker"),
             Map.entry("doCatch", "doTry"),
             Map.entry("doFinally", "doTry"),
+            Map.entry("case", "switch"),
+            Map.entry("switchOtherwise", "switch"),
             Map.entry("when", "choice"),
             Map.entry("otherwise", "choice"),
             Map.entry("onWhen", "onException"),
@@ -107,6 +109,8 @@ public final class CatalogSamples {
             Map.entry("retry", "onException"), Map.entry("error handling", 
"onException"),
             Map.entry("errorhandling", "onException"), Map.entry("exception", 
"onException"),
             Map.entry("route by content", "choice"), Map.entry("if", "choice"),
+            Map.entry("route by value", "switch"), Map.entry("switch case", 
"switch"),
+            Map.entry("decision table", "switch"),
             Map.entry("parallel", "multicast"), Map.entry("resilience", 
"circuitBreaker"),
             Map.entry("rest api", "rest"), Map.entry("http server", "rest"), 
Map.entry("endpoint", "rest"),
             Map.entry("convert", "convertBodyTo"), Map.entry("json", 
"marshal"),
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
index b4674d69cdae..0d493a50d94e 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
@@ -750,7 +750,7 @@ public final class ToolRegistry {
                 }));
 
         register(tool("catalog_eips",
-                "Search EIPs (Enterprise Integration Patterns) like split, 
aggregate, filter, choice, multicast.")
+                "Search EIPs (Enterprise Integration Patterns) like split, 
aggregate, filter, choice, switch, multicast.")
                 .param("filter", "string",
                         "Filter by name, title, or description 
(case-insensitive substring)", false)
                 .executor((ctx, args) -> {
@@ -909,7 +909,7 @@ public final class ToolRegistry {
         register(tool("catalog_eip_doc",
                 "Get detailed documentation for a Camel EIP (Enterprise 
Integration Pattern).")
                 .param("eip", "string",
-                        "EIP name (e.g., split, aggregate, filter, choice)", 
true)
+                        "EIP name (e.g., split, aggregate, filter, choice, 
switch)", true)
                 .executor((ctx, args) -> {
                     String name = args.get("eip");
                     if (name == null || name.isBlank()) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
index 669663a3f1b1..091b463f93d8 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
@@ -89,6 +89,27 @@ public class CatalogSamplesTest {
         assertThat(CatalogSamples.INTENTS).doesNotContainKeys("fan out", 
"broadcast", "router", "fallback", "split");
     }
 
+    @Test
+    void switchIntentsAliasesAndPartsReturnTheDispatchSample() {
+        CamelCatalog catalog = new DefaultCamelCatalog();
+        for (String term : List.of("route by value", "switch case", "decision 
table",
+                "decision-table", "dispatch-table", "lookup-table", "case", 
"switchOtherwise", "switch-otherwise")) {
+            JsonObject sample = CatalogSamples.sample(catalog, "eip", term, 1);
+            assertThat(sample.getString("name")).as(term).isEqualTo("switch");
+            assertThat(yaml(sample, 0)).contains("switch:", "uri: 
direct:billing", "uri: direct:technical",
+                    "uri: direct:review");
+        }
+        for (String term : List.of("case", "switchOtherwise")) {
+            assertThat(CatalogSamples.sample(catalog, term, 
1).getString("partOf")).isEqualTo("switch");
+        }
+        for (String alias : List.of("decision-table", "dispatch-table", 
"lookup-table")) {
+            assertThat(catalog.suggestEipNames(alias, 
1)).containsExactly("switch");
+        }
+        assertThat(CatalogSamples.sample(catalog, "route by content", 
1).getString("name")).isEqualTo("choice");
+        assertThat(CatalogSamples.sample(catalog, "if", 
1).getString("name")).isEqualTo("choice");
+        assertThat(CatalogSamples.sample(catalog, "lookup", 
1).getString("name")).isEqualTo("enrich");
+    }
+
     @Test
     void unknownNameGetsSuggestions() {
         JsonObject o = CatalogSamples.sample("aggregat", 2);
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
index 21f954d34b30..e3d5c36922a2 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
@@ -63,7 +63,7 @@ public class PromptDefinitions {
                 Call `camel_catalog_find` with the protocol or product 
(kind="component") to find matching components.
 
                 ### Step 2: Identify EIPs
-                Determine which Enterprise Integration Patterns are needed 
(e.g., split, aggregate, filter, choice).
+                Determine which Enterprise Integration Patterns are needed 
(e.g., split, aggregate, filter, choice, switch).
                 Call `camel_catalog_find` with kind="eip" and a term such as 
fan-out, dedup or retry to find \
                 matching patterns; `camel_catalog_sample` gives a validated 
YAML sample of one.
 
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/SwitchSchemaTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/SwitchSchemaTest.java
index 9d853b194343..e40382ee5c2d 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/SwitchSchemaTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/SwitchSchemaTest.java
@@ -42,6 +42,15 @@ class SwitchSchemaTest {
                               uri: direct:review
                 """;
         assertThat(validator.validate(route)).isEmpty();
+        assertThat(validator.validate(route.replace("value: billing", "id: 
billingCase\n                value: \"001\"")))
+                .isEmpty();
+        assertThat(validator.validate(route.replace("uri: direct:billing",
+                "uri: direct\n                parameters:\n                  
name: billing")
+                .replace("uri: direct:review", "uri: direct\n              
parameters:\n                name: review")))
+                .isEmpty();
+        assertThat(validator.validate(route.replace("                uri: 
direct:billing\n", ""))).isNotEmpty();
+        assertThat(validator.validate(route.replace("uri: direct:billing", 
"steps: [{to: {uri: direct:billing}}]")))
+                .isNotEmpty();
         assertThat(validator.validate(route.replace("case:", "keys: 
[department, urgent]\n            case:")))
                 .isNotEmpty();
         assertThat(validator.validate(route.replace("value: billing", "values: 
[department: billing]")))
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 320e9d67ea9f..eacf7a4d52cd 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
@@ -8369,6 +8369,7 @@
       "title" : "Switch",
       "description" : "Evaluates a selector once and dispatches to a fixed 
endpoint by literal scalar values",
       "label" : "eip,routing",
+      "aliases" : [ "decision-table", "dispatch-table", "lookup-table", "case" 
],
       "input" : true,
       "isListItem" : true,
       "children" : [ {
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SwitchTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SwitchTest.java
index 6150f2182916..57aa88df7242 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SwitchTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SwitchTest.java
@@ -30,49 +30,11 @@ import static 
org.assertj.core.api.Assertions.assertThatThrownBy;
 
 class SwitchTest extends YamlTestSupport {
     @Test
-    void scalarValuesAndEndpointParametersRoundTrip() throws Exception {
-        loadRoutes("""
-                - route:
-                    id: decision
-                    from:
-                      uri: direct:start
-                      steps:
-                        - switch:
-                            selector:
-                              header:
-                                expression: decision
-                            case:
-                              - value: urgent
-                                uri: direct
-                                parameters:
-                                  name: urgent
-                              - value: billing
-                                uri: direct:billing
-                            otherwise:
-                              uri: direct
-                              parameters:
-                                name: review
-                - route:
-                    from:
-                      uri: direct:urgent
-                      steps:
-                        - setBody:
-                            constant: urgent
-                - route:
-                    from:
-                      uri: direct:billing
-                      steps:
-                        - setBody:
-                            constant: billing
-                - route:
-                    from:
-                      uri: direct:review
-                      steps:
-                        - setBody:
-                            constant: review
-                """);
+    void resourceWithScalarValuesAndEndpointParametersRoundTrips() throws 
Exception {
+        loadRoutes(ResourceHelper.resolveResource(context, 
"classpath:switch.camel.yaml"));
         SwitchDefinition sw = (SwitchDefinition) 
context.getRouteDefinition("decision").getOutputs().get(0);
         assertThat(sw.getCases().get(0).getUri()).isEqualTo("direct:urgent");
+        assertThat(sw.getCases().get(0).getId()).isEqualTo("urgentCase");
         assertThat(sw.getCases().get(0).getValue()).isEqualTo("urgent");
         assertThat(sw.getOtherwise().getUri()).isEqualTo("direct:review");
         try (var restored = new DefaultCamelContext()) {
@@ -80,6 +42,8 @@ class SwitchTest extends YamlTestSupport {
                 String yaml = new 
LwModelToYAMLDumper().dumpModelAsYaml(context, route);
                 
PluginHelper.getRoutesLoader(restored).loadRoutes(ResourceHelper.fromString(route.getId()
 + ".yaml", yaml));
             }
+            SwitchDefinition restoredSwitch = (SwitchDefinition) 
restored.getRouteDefinition("decision").getOutputs().get(0);
+            
assertThat(restoredSwitch.getCases().get(0).getId()).isEqualTo("urgentCase");
             restored.start();
             try (var template = restored.createProducerTemplate()) {
                 assertThat(template.requestBodyAndHeader("direct:start", 
"original", "decision",
@@ -88,6 +52,7 @@ class SwitchTest extends YamlTestSupport {
                         "BILLING")).isEqualTo("billing");
                 assertThat(template.requestBodyAndHeader("direct:start", 
"original", "decision",
                         "other")).isEqualTo("review");
+                assertThat(template.requestBody("direct:start", 
"original")).isEqualTo("review");
             }
         }
     }
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/resources/switch.camel.yaml 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/resources/switch.camel.yaml
new file mode 100644
index 000000000000..f5351393d026
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/resources/switch.camel.yaml
@@ -0,0 +1,56 @@
+#
+# 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:
+    id: decision
+    from:
+      uri: direct:start
+      steps:
+        - switch:
+            selector:
+              header:
+                expression: decision
+            case:
+              - id: urgentCase
+                value: urgent
+                uri: direct
+                parameters:
+                  name: urgent
+              - value: billing
+                uri: direct:billing
+            otherwise:
+              uri: direct
+              parameters:
+                name: review
+- route:
+    from:
+      uri: direct:urgent
+      steps:
+        - setBody:
+            constant: urgent
+- route:
+    from:
+      uri: direct:billing
+      steps:
+        - setBody:
+            constant: billing
+- route:
+    from:
+      uri: direct:review
+      steps:
+        - setBody:
+            constant: review

Reply via email to