Repository: smithy-lang/smithy (java)

Issue:
When generating openapi spec, examples trait does not respect jsonName trait
When using the [converting Smithy to OpenAPI](https://smithy.io/2.0/guides/model-translations/converting-to-openapi.html#converting-smithy-to-openapi), generation of [examples](https://swagger.io/docs/specification/v3_0/adding-examples/) from the [`@examples`](https://smithy.io/2.0/spec/documentation-traits.html#examples-trait) documentation trait the [`@jsonName`](https://smithy.io/2.0/spec/protocol-traits.html#jsonname-trait) trait is not respected, leading to generation of examples which don't comply with the generated spec.

I adapted the `quickstart-cli` template project to showcase the problem: https://github.com/ingemaradahl/smithy-openapi-example/commit/dfada352c19bdcf40e96207985cb08f7639792ab. The [generated OpenAPI spec](https://gist.github.com/ingemaradahl/a9a16d978952205016806c1bbf81c035#file-weather-openapi-json-L212l) correctly uses the `jsonName` when generating the component schemas, but in the [example](https://gist.github.com/ingemaradahl/a9a16d978952205016806c1bbf81c035#file-weather-openapi-json-L95) uses the original smithy structure member name.

It is possible to use the same value as the `jsonName` trait argument in the example, but this leads to warnings issued when running `smithy build`. When incorporating in build tools, these warnings are sometimes treated as errors:
```
──  WARNING  ── ExamplesTrait.UnknownMember.example.weather#CityCoordinates.coordinate_system
Shape: example.weather#CreateCity
File:  models/weather.smithy:99:22

99|         coordinates: {
  |                      ^

Example input of Create a city.coordinates: Member coordinate_system does
not exist in example.weather#CityCoordinates
```

Pull request description:
Fix jsonName conversion for OpenAPI examples
When converting to OpenAPI for the restJson1 protocol, we convert member names if they have the jsonName trait. However, we were *not* converting those member names in examples. This updates example conversion to respect member name, if present and enabled.

Fixes #2659

---
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 825752697..dd958994f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,53 @@
 # Smithy Changelog
 
+## 1.60.2 (2025-06-26)
+
+### Bug Fixes
+
+* Added model discovery args to smithy select. ([#2680](https://github.com/smithy-lang/smithy/pull/2680))
+* Fix `NonQueryCompatible` to remove the bogus empty JSON body and media-type. ([#2681](https://github.com/smithy-lang/smithy/pull/2681))
+* Reduced query error uniqueness validation severity. ([#2682](https://github.com/smithy-lang/smithy/pull/2682))
+
+## 1.60.1 (2025-06-25)
+
+### Bug Fixes
+
+* Fixed body of awsQueryCompatible test. ([#2677](https://github.com/smithy-lang/smithy/pull/2677))
+
+## 1.60.0 (2025-06-23)
+
+### Bug Fixes
+
+* Added ASM to the relocated dependencies. ([#2676](https://github.com/smithy-lang/smithy/pull/2676))
+
+### Features
+
+* Added protocol tests for `@awsQueryCompatible`. ([#2672](https://github.com/smithy-lang/smithy/pull/2672))
+* Added validation to ensure query errors are unique. ([#2674](https://github.com/smithy-lang/smithy/pull/2674))
+
+## 1.59.0 (2025-06-16)
+
+### Bug Fixes
+
+* Fixed generation of nested lists/maps in trait code generation. ([#2647](https://github.com/smithy-lang/smithy/pull/2647))
+* Fixed generation of boolean collections in trait code generation. ([#2652](https://github.com/smithy-lang/smithy/pull/2652))
+* Fixed enum generation in docgen. ([#2653](https://github.com/smithy-lang/smithy/pull/2653))
+
+### Features
+
+* Added a warning when mixin members are removed. ([#2644](https://github.com/smithy-lang/smithy/pull/2644))
+* Added a tag to identify service-specific protocol tests. ([#2655](https://github.com/smithy-lang/smithy/pull/2655))
+
+### Documentation
+
+* Made several improvements to the landing pages. ([#2656](https://github.com/smithy-lang/smithy/pull/2656))
+* Added guidance about generating unknown members for unions. ([#2657](https://github.com/smithy-lang/smithy/pull/2657))
+* Updated references to `awslabs` to `smithy-lang` where relevant. ([#2662](https://github.com/smithy-lang/smithy/pull/2662))
+
+### Other
+
+* Added additional protocol tests for `restJson1`. ([#2641](https://github.com/smithy-lang/smithy/pull/2641))
+
 ## 1.58.0 (2025-05-13)
 
 ### Bug Fixes
diff --git a/VERSION b/VERSION
index 79f82f6b8..bb399dd22 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-1.58.0
+1.60.2
diff --git a/gradle.properties b/gradle.properties
index d8499eaed..54e1c6245 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
 org.gradle.configureondmand=true
 org.gradle.jvmargs='-Dfile.encoding=UTF-8'
 org.gradle.parallel=true
-smithyGradleVersion=1.2.0
+smithyGradleVersion=1.3.0
diff --git a/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/protocols/QueryErrorCodeValidator.java b/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/protocols/QueryErrorCodeValidator.java
new file mode 100644
index 000000000..5db5d8902
--- /dev/null
+++ b/smithy-aws-traits/src/main/java/software/amazon/smithy/aws/traits/protocols/QueryErrorCodeValidator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package software.amazon.smithy.aws.traits.protocols;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import software.amazon.smithy.model.Model;
+import software.amazon.smithy.model.knowledge.TopDownIndex;
+import software.amazon.smithy.model.shapes.OperationShape;
+import software.amazon.smithy.model.shapes.ServiceShape;
+import software.amazon.smithy.model.shapes.Shape;
+import software.amazon.smithy.model.shapes.ShapeId;
+import software.amazon.smithy.model.validation.AbstractValidator;
+import software.amazon.smithy.model.validation.ValidationEvent;
+import software.amazon.smithy.utils.SmithyInternalApi;
+
+/**
+ * Emits a warning if two or more error shapes share the same error code.
+ */
+@SmithyInternalApi
+public class QueryErrorCodeValidator extends AbstractValidator {
+
+    @Override
+    public List<ValidationEvent> validate(Model model) {
+        List<ValidationEvent> events = Collections.emptyList();
+        for (ServiceShape service : model.getServiceShapes()) {
+            if (service.hasTrait(AwsQueryCompatibleTrait.class) || service.hasTrait(AwsQueryTrait.class)
+                    || service.hasTrait(Ec2QueryTrait.class)) {
+                List<ValidationEvent> serviceEvents = validateService(model, service);
+                if (!serviceEvents.isEmpty()) {
+                    if (events.isEmpty()) {
+                        events = new ArrayList<>();
+                    }
+                    events.addAll(serviceEvents);
+                }
+            }
+        }
+        return events;
+    }
+
+    private List<ValidationEvent> validateService(Model model, ServiceShape service) {
+        TopDownIndex index = TopDownIndex.of(model);
+        Set<ShapeId> errors = new HashSet<>(service.getErrors());
+        for (OperationShape operation : index.getContainedOperations(service)) {
+            errors.addAll(operation.getErrors());
+        }
+
+        Map<String, Set<String>> errorCodeBindings = new HashMap<>();
+        for (ShapeId errorId : errors) {
+            String errorCode = errorId.getName();
+            Shape errorShape = model.expectShape(errorId);
+            if (errorShape.hasTrait(AwsQueryErrorTrait.class)) {
+                AwsQueryErrorTrait trait = errorShape.expectTrait(AwsQueryErrorTrait.class);
+                errorCode = trait.getCode();
+            }
+            errorCodeBindings.computeIfAbsent(errorCode, k -> new TreeSet<>()).add(errorId.toString());
+        }
+        List<ValidationEvent> events = Collections.emptyList();
+        for (Map.Entry<String, Set<String>> entry : errorCodeBindings.entrySet()) {
+            if (entry.getValue().size() == 1) {
+                continue;
+            }
+            String errorCode = entry.getKey();
+            String shapes = String.join(", ", entry.getValue());
+            if (events.isEmpty()) {
+                events = new ArrayList<>();
+            }
+            events.add(warning(service,
+                    String.format("Multiple error shapes with the error code `%s` found: [%s]. This error code "
+                            + "ambiguity will result in the wrong error shape being deserialized when running the "
+                            + "query protocol. This pain will be carried forward when migrating to a new protocol "
+                            + "when using the awsQueryCompatible trait.", errorCode, shapes)));
+        }
+        return events;
+    }
+}
diff --git a/smithy-aws-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator b/smithy-aws-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator
index 133106fc8..76bf941d9 100644
--- a/smithy-aws-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator
+++ b/smithy-aws-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator
@@ -5,6 +5,7 @@ software.amazon.smithy.aws.traits.SdkServiceIdValidator
 software.amazon.smithy.aws.traits.clientendpointdiscovery.ClientEndpointDiscoveryValidator
 software.amazon.smithy.aws.traits.protocols.ProtocolHttpValidator
 software.amazon.smithy.aws.traits.EventSourceValidator
+software.amazon.smithy.aws.traits.protocols.QueryErrorCodeValidator
 software.amazon.smithy.aws.traits.protocols.ProtocolHttpPayloadValidator
 software.amazon.smithy.aws.traits.tagging.ServiceTaggingValidator
 software.amazon.smithy.aws.traits.tagging.TagEnabledServiceValidator
diff --git a/smithy-cli/build.gradle.kts b/smithy-cli/build.gradle.kts
index f838a7150..d28c4a933 100644
--- a/smithy-cli/build.gradle.kts
+++ b/smithy-cli/build.gradle.kts
@@ -191,6 +191,7 @@ tasks {
         relocate("org.apache", "software.amazon.smithy.cli.shaded.apache")
         relocate("org.sonatype", "software.amazon.smithy.cli.shaded.sonatype")
         relocate("org.codehaus", "software.amazon.smithy.cli.shaded.codehaus")
+        relocate("org.objectweb", "software.amazon.smithy.cli.shaded.objectweb")
 
         // If other javax packages are ever pulled in, we'll need to update this list. This is more deliberate about
         // what's shaded to ensure that things like javax.net.ssl.SSLSocketFactory are not inadvertently shaded.
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SelectCommand.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SelectCommand.java
index cc59387d4..9542423ec 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SelectCommand.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SelectCommand.java
@@ -62,6 +62,7 @@ final class SelectCommand implements Command {
     @Override
     public int execute(Arguments arguments, Env env) {
         arguments.addReceiver(new ConfigOptions());
+        arguments.addReceiver(new DiscoveryOptions());
         arguments.addReceiver(new BuildOptions());
         arguments.addReceiver(new Options());
 
diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AbstractRestProtocol.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AbstractRestProtocol.java
index dcd550617..a6d2a82ce 100644
--- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AbstractRestProtocol.java
+++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AbstractRestProtocol.java
@@ -109,11 +109,16 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
             MessageType messageType
     );
 
+    @Deprecated
+    Node transformSmithyValueToProtocolValue(Node value) {
+        return value;
+    }
+
     /**
      * Converts Smithy values in Node form to a data exchange format used by a protocol (e.g., XML).
      * Then returns the converted value as a long string (escaping where necessary).
      * If data exchange format is JSON (e.g., as in restJson1 protocol),
-     * method should return values without any modification.
+     * method should respect the jsonName trait, but otherwise not modify the node.
      *
      * <p> Used for the value property of OpenAPI example objects.
      * For protocols that do not use JSON as data-exchange format,
@@ -122,10 +127,14 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
      * E.g., for restXML protocol, values would be converted to a large String of XML value / object,
      * escaping where necessary.
      *
-     * @param value    value to be converted.
+     * @param context Conversion context.
+     * @param shape The shape that the value represents.
+     * @param value value to be converted.
      * @return  the long string (escaped where necessary) of values in a data exchange format used by a protocol.
      */
-    abstract Node transformSmithyValueToProtocolValue(Node value);
+    Node transformSmithyValueToProtocolValue(Context<T> context, Shape shape, Node value) {
+        return value;
+    }
 
     @Override
     public Set<String> getProtocolRequestHeaders(Context<T> context, OperationShape operationShape) {
@@ -204,6 +213,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
                     .in("path")
                     .schema(schema)
                     .examples(createExamplesForMembersWithHttpTraits(
+                            context,
                             operation,
                             binding,
                             MessageType.REQUEST,
@@ -219,6 +229,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
      * path parameters, query parameters, header parameters, and payload.
      */
     private Map<String, Node> createExamplesForMembersWithHttpTraits(
+            Context<T> context,
             Shape operationOrError,
             HttpBinding binding,
             MessageType type,
@@ -229,15 +240,17 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
         }
 
         if (type == MessageType.ERROR) {
-            return createErrorExamplesForMembersWithHttpTraits(operationOrError, binding, operation);
+            return createErrorExamplesForMembersWithHttpTraits(context, operationOrError, binding, operation);
         } else {
             Map<String, Node> examples = new TreeMap<>();
             // unique numbering for unique example names in OpenAPI.
             int uniqueNum = 1;
 
-            Optional<ExamplesTrait> examplesTrait = operationOrError.getTrait(ExamplesTrait.class);
-            for (ExamplesTrait.Example example : examplesTrait.map(ExamplesTrait::getExamples)
-                    .orElse(Collections.emptyList())) {
+            List<ExamplesTrait.Example> modeledExamples = operationOrError.getTrait(ExamplesTrait.class)
+                    .map(ExamplesTrait::getExamples)
+                    .orElse(Collections.emptyList());
+
+            for (ExamplesTrait.Example example : modeledExamples) {
                 ObjectNode inputOrOutput = type == MessageType.REQUEST ? example.getInput()
                         : example.getOutput().orElse(Node.objectNode());
                 String name = operationOrError.getId().getName() + "_example" + uniqueNum++;
@@ -251,7 +264,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
                             ExampleObject.builder()
                                     .summary(example.getTitle())
                                     .description(example.getDocumentation().orElse(""))
-                                    .value(transformSmithyValueToProtocolValue(values))
+                                    .value(transformSmithyValueToProtocolValue(context, binding.getMember(), values))
                                     .build()
                                     .toNode());
                 }
@@ -264,6 +277,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
      * Helper method for createExamples() method.
      */
     private Map<String, Node> createErrorExamplesForMembersWithHttpTraits(
+            Context<T> context,
             Shape error,
             HttpBinding binding,
             OperationShape operation
@@ -290,7 +304,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
                         ExampleObject.builder()
                                 .summary(example.getTitle())
                                 .description(example.getDocumentation().orElse(""))
-                                .value(transformSmithyValueToProtocolValue(values))
+                                .value(transformSmithyValueToProtocolValue(context, binding.getMember(), values))
                                 .build()
                                 .toNode());
             }
@@ -302,6 +316,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
      * This method is used for converting the Smithy examples to OpenAPI examples for non-payload HTTP message body.
      */
     private Map<String, Node> createBodyExamples(
+            Context<T> context,
             Shape operationOrError,
             List<HttpBinding> bindings,
             MessageType type,
@@ -312,7 +327,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
         }
 
         if (type == MessageType.ERROR) {
-            return createErrorBodyExamples(operationOrError, bindings, operation);
+            return createErrorBodyExamples(context, operationOrError, bindings, operation);
         } else {
             Map<String, Node> examples = new TreeMap<>();
             // unique numbering for unique example names in OpenAPI.
@@ -321,18 +336,30 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
             Optional<ExamplesTrait> examplesTrait = operationOrError.getTrait(ExamplesTrait.class);
             for (ExamplesTrait.Example example : examplesTrait.map(ExamplesTrait::getExamples)
                     .orElse(Collections.emptyList())) {
+
+                Shape structure = operationOrError;
+                if (operationOrError.isOperationShape()) {
+                    OperationShape op = operationOrError.asOperationShape().get();
+                    if (type == MessageType.REQUEST) {
+                        structure = context.getModel().expectShape(op.getInputShape());
+                    } else {
+                        structure = context.getModel().expectShape(op.getOutputShape());
+                    }
+                }
+
                 // get members included in bindings
                 ObjectNode values = getMembersWithHttpBindingTrait(bindings,
                         type == MessageType.REQUEST ? example.getInput()
                                 : example.getOutput().orElse(Node.objectNode()));
                 String name = operationOrError.getId().getName() + "_example" + uniqueNum++;
+
                 // this if condition is needed to avoid errors when converting examples of response.
                 if (!example.getError().isPresent() || type == MessageType.REQUEST) {
                     examples.put(name,
                             ExampleObject.builder()
                                     .summary(example.getTitle())
                                     .description(example.getDocumentation().orElse(""))
-                                    .value(transformSmithyValueToProtocolValue(values))
+                                    .value(transformSmithyValueToProtocolValue(context, structure, values))
                                     .build()
                                     .toNode());
                 }
@@ -342,6 +369,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
     }
 
     private Map<String, Node> createErrorBodyExamples(
+            Context<T> context,
             Shape error,
             List<HttpBinding> bindings,
             OperationShape operation
@@ -362,7 +390,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
                         ExampleObject.builder()
                                 .summary(example.getTitle())
                                 .description(example.getDocumentation().orElse(""))
-                                .value(transformSmithyValueToProtocolValue(values))
+                                .value(transformSmithyValueToProtocolValue(context, error, values))
                                 .build()
                                 .toNode());
             }
@@ -456,7 +484,8 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
             }
 
             param.schema(createQuerySchema(context, member, target));
-            param.examples(createExamplesForMembersWithHttpTraits(operation, binding, MessageType.REQUEST, null));
+            param.examples(
+                    createExamplesForMembersWithHttpTraits(context, operation, binding, MessageType.REQUEST, null));
             result.add(param.build());
         }
 
@@ -496,7 +525,8 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
                 param.in(null).name(null);
             }
 
-            param.examples(createExamplesForMembersWithHttpTraits(operationOrError, binding, messageType, operation));
+            param.examples(
+                    createExamplesForMembersWithHttpTraits(context, operationOrError, binding, messageType, operation));
 
             // Create the appropriate schema based on the shape type.
             Shape target = context.getModel().expectShape(member.getTarget());
@@ -566,6 +596,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
             return shapeName + "InputPayload";
         }).toBuilder()
                 .examples(createExamplesForMembersWithHttpTraits(
+                        context,
                         operation,
                         binding,
                         MessageType.REQUEST,
@@ -598,7 +629,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
         String pointer = context.putSynthesizedSchema(synthesizedName, schema);
         MediaTypeObject mediaTypeObject = MediaTypeObject.builder()
                 .schema(Schema.builder().ref(pointer).build())
-                .examples(createBodyExamples(operation, bindings, MessageType.REQUEST, null))
+                .examples(createBodyExamples(context, operation, bindings, MessageType.REQUEST, null))
                 .build();
 
         // If any of the top level bindings are required, then the body itself must be required.
@@ -757,6 +788,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
                     : shapeName + "ErrorPayload";
         }).toBuilder()
                 .examples(createExamplesForMembersWithHttpTraits(
+                        context,
                         operationOrError,
                         binding,
                         type,
@@ -840,7 +872,7 @@ abstract class AbstractRestProtocol<T extends Trait> implements OpenApiProtocol<
         String pointer = context.putSynthesizedSchema(synthesizedName, schema);
         MediaTypeObject mediaTypeObject = MediaTypeObject.builder()
                 .schema(Schema.builder().ref(pointer).build())
-                .examples(createBodyExamples(operationOrError, bindings, messageType, operation))
+                .examples(createBodyExamples(context, operationOrError, bindings, messageType, operation))
                 .build();
 
         responseBuilder.putContent(mediaType, mediaTypeObject);
diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AwsRestJson1Protocol.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AwsRestJson1Protocol.java
index bbde5e404..3f84277ac 100644
--- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AwsRestJson1Protocol.java
+++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AwsRestJson1Protocol.java
@@ -166,7 +166,7 @@ public final class AwsRestJson1Protocol extends AbstractRestProtocol<RestJson1Tr
     }
 
     @Override
-    Node transformSmithyValueToProtocolValue(Node value) {
-        return value;
+    Node transformSmithyValueToProtocolValue(Context<RestJson1Trait> context, Shape shape, Node value) {
+        return value.accept(new JsonValueNodeTransformer(context, shape));
     }
 }
diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/JsonValueNodeTransformer.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/JsonValueNodeTransformer.java
new file mode 100644
index 000000000..62f4df56e
--- /dev/null
+++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/JsonValueNodeTransformer.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package software.amazon.smithy.openapi.fromsmithy.protocols;
+
+import java.util.Map;
+import software.amazon.smithy.model.node.ArrayNode;
+import software.amazon.smithy.model.node.BooleanNode;
+import software.amazon.smithy.model.node.Node;
+import software.amazon.smithy.model.node.NodeVisitor;
+import software.amazon.smithy.model.node.NullNode;
+import software.amazon.smithy.model.node.NumberNode;
+import software.amazon.smithy.model.node.ObjectNode;
+import software.amazon.smithy.model.node.StringNode;
+import software.amazon.smithy.model.shapes.MapShape;
+import software.amazon.smithy.model.shapes.MemberShape;
+import software.amazon.smithy.model.shapes.Shape;
+import software.amazon.smithy.model.traits.JsonNameTrait;
+import software.amazon.smithy.openapi.fromsmithy.Context;
+
+/**
+ * Applies the jsonName trait to a node value if applicable.
+ */
+public class JsonValueNodeTransformer implements NodeVisitor<Node> {
+    private final Context<?> context;
+    private final Shape shape;
+
+    /**
+     * Construct a JsonValueNodeTransformer.
+     *
+     * @param context Conversion context. Used to determine if jsonName should be used.
+     * @param shape The shape of the node being converted.
+     */
+    public JsonValueNodeTransformer(Context<?> context, Shape shape) {
+        this.context = context;
+        this.shape = shape;
+    }
+
+    @Override
+    public Node booleanNode(BooleanNode node) {
+        return node;
+    }
+
+    @Override
+    public Node nullNode(NullNode node) {
+        return node;
+    }
+
+    @Override
+    public Node numberNode(NumberNode node) {
+        return node;
+    }
+
+    @Override
+    public Node stringNode(StringNode node) {
+        return node;
+    }
+
+    @Override
+    public Node arrayNode(ArrayNode node) {
+        ArrayNode.Builder resultBuilder = ArrayNode.builder();
+        Shape listShape = shape.asMemberShape()
+                .map(m -> context.getModel().expectShape(m.getTarget()))
+                .orElse(shape);
+
+        Shape target = context.getModel().expectShape(listShape.asListShape().get().getMember().getTarget());
+        JsonValueNodeTransformer elementTransformer = new JsonValueNodeTransformer(context, target);
+        for (Node element : node.getElements()) {
+            resultBuilder.withValue(element.accept(elementTransformer));
+        }
+        return resultBuilder.build();
+    }
+
+    @Override
+    public Node objectNode(ObjectNode node) {
+        Shape actual = shape.asMemberShape()
+                .map(m -> context.getModel().expectShape(m.getTarget()))
+                .orElse(shape);
+
+        if (shape.isMapShape()) {
+            return mapNode(actual.asMapShape().get(), node);
+        }
+        return structuredNode(actual, node);
+    }
+
+    private Node structuredNode(Shape structure, ObjectNode node) {
+        ObjectNode.Builder resultBuilder = ObjectNode.builder();
+        for (Map.Entry<StringNode, Node> entry : node.getMembers().entrySet()) {
+            String key = entry.getKey().getValue();
+            if (structure.getMember(key).isPresent()) {
+                MemberShape member = structure.getMember(key).get();
+                Shape target = context.getModel().expectShape(member.getTarget());
+                JsonValueNodeTransformer entryTransformer = new JsonValueNodeTransformer(context, target);
+                resultBuilder.withMember(getKey(member), entry.getValue().accept(entryTransformer));
+            } else {
+                resultBuilder.withMember(key, entry.getValue());
+            }
+        }
+        return resultBuilder.build();
+    }
+
+    private String getKey(MemberShape member) {
+        if (!context.getJsonSchemaConverter().getConfig().getUseJsonName()) {
+            return member.getMemberName();
+        }
+        return member.getTrait(JsonNameTrait.class)
+                .map(JsonNameTrait::getValue)
+                .orElse(member.getMemberName());
+    }
+
+    private Node mapNode(MapShape map, ObjectNode node) {
+        ObjectNode.Builder resultBuilder = ObjectNode.builder();
+        Shape target = context.getModel().expectShape(map.getValue().getTarget());
+        JsonValueNodeTransformer entryTransformer = new JsonValueNodeTransformer(context, target);
+        for (Map.Entry<StringNode, Node> entry : node.getMembers().entrySet()) {
+            resultBuilder.withMember(entry.getKey(), entry.getValue().accept(entryTransformer));
+        }
+        return resultBuilder.build();
+    }
+}


Test patch:
diff --git a/smithy-aws-protocol-tests/model/aws-config.smithy b/smithy-aws-protocol-tests/model/aws-config.smithy
index 321956d62..1679c0944 100644
--- a/smithy-aws-protocol-tests/model/aws-config.smithy
+++ b/smithy-aws-protocol-tests/model/aws-config.smithy
@@ -109,3 +109,30 @@ enum RetryMode {
     STANDARD = "standard"
     ADAPTIVE = "adaptive"
 }
+
+/// Vendor params to use when making assertions about error code or query
+/// error type.
+structure ErrorCodeParams {
+    /// The error code exposed to the customer.
+    ///
+    /// If the SDK exposes the error code to customers, this value MUST be
+    /// asserted to match the exposed error code.
+    ///
+    /// This is modifiable by the awsQueryError trait. For services with
+    /// awsQueryCompatible, it will be exposed in the `x-amzn-query-error`
+    /// header which takes the form `Code;Fault`. This value represents
+    /// the `Code` portion.
+    @required
+    code: String
+
+    /// The query error type exposed to the customer.
+    ///
+    /// If the SDK exposes the query error type to customers, this value MUST be
+    /// asserted to match the exposed query error type.
+    ///
+    /// This is modifiable by the awsQueryError trait. For services with
+    /// awsQueryCompatible, it will be exposed in the `x-amzn-query-error`
+    /// header which takes the form `Code;Fault`. This value represents
+    /// the `Fault` portion.
+    type: String
+}
diff --git a/smithy-aws-protocol-tests/model/awsJson1_0/main.smithy b/smithy-aws-protocol-tests/model/awsJson1_0/main.smithy
index f7314548b..4dcabeaec 100644
--- a/smithy-aws-protocol-tests/model/awsJson1_0/main.smithy
+++ b/smithy-aws-protocol-tests/model/awsJson1_0/main.smithy
@@ -5,8 +5,7 @@ namespace aws.protocoltests.json10
 use aws.api#service
 use aws.auth#sigv4
 use aws.protocols#awsJson1_0
-use smithy.test#httpRequestTests
-use smithy.test#httpResponseTests
+use aws.protocols#awsQueryCompatible
 
 @service(sdkId: "JSON RPC 10")
 @sigv4(name: "jsonrpc10")
@@ -42,5 +41,20 @@ service JsonRpc10 {
         OperationWithRequiredMembers,
         OperationWithNestedStructure
         OperationWithRequiredMembersWithDefaults
+
+        QueryIncompatibleOperation
+    ]
+}
+
+
+@service(sdkId: "Query Compatible JSON RPC 10")
+@sigv4(name: "querycompatiblejsonrpc10")
+@awsJson1_0
+@title("Query Compatible Json 1.0 Protocol Service")
+@awsQueryCompatible
+service QueryCompatibleJsonRpc10 {
+    version: "2020-07-14",
+    operations: [
+        QueryCompatibleOperation
     ]
 }
diff --git a/smithy-aws-protocol-tests/model/awsJson1_0/query-compatible.smithy b/smithy-aws-protocol-tests/model/awsJson1_0/query-compatible.smithy
new file mode 100644
index 000000000..dc30be6a2
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/awsJson1_0/query-compatible.smithy
@@ -0,0 +1,94 @@
+$version: "2.0"
+
+namespace aws.protocoltests.json10
+
+use aws.protocols#awsJson1_0
+use aws.protocols#awsQueryError
+use aws.protocoltests.config#ErrorCodeParams
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+@httpRequestTests([
+    {
+        id: "NonQueryCompatibleAwsJson10ForbidsQueryModeHeader"
+        documentation: "The query mode header MUST NOT be set on non-query-compatible services."
+        protocol: awsJson1_0
+        method: "POST"
+        headers: { "Content-Type": "application/x-amz-json-1.0", "X-Amz-Target": "JsonRpc10.QueryIncompatibleOperation" }
+        uri: "/"
+        body: "{}"
+        bodyMediaType: "application/json"
+    }
+])
+@idempotent
+operation QueryIncompatibleOperation {}
+
+@idempotent
+operation QueryCompatibleOperation {
+    errors: [
+        NoCustomCodeError
+        CustomCodeError
+    ]
+}
+
+apply QueryCompatibleOperation @httpRequestTests([
+    {
+        id: "QueryCompatibleAwsJson10CborSendsQueryModeHeader"
+        documentation: "Clients for query-compatible services MUST send the x-amzn-query-mode header."
+        protocol: awsJson1_0
+        method: "POST"
+        headers: { "Content-Type": "application/x-amz-json-1.0", "x-amzn-query-mode": "true", "X-Amz-Target": "QueryCompatibleJsonRpc10.QueryCompatibleOperation" }
+        uri: "/"
+        body: "{}"
+        bodyMediaType: "application/json"
+    }
+])
+
+@error("client")
+structure NoCustomCodeError {
+    message: String
+}
+
+apply NoCustomCodeError @httpResponseTests([
+    {
+        id: "QueryCompatibleAwsJson10CborNoCustomCodeError"
+        documentation: "Parses simple errors with no query error code"
+        protocol: awsJson1_0
+        params: { message: "Hi" }
+        code: 400
+        headers: { "Content-Type": "application/x-amz-json-1.0" }
+        body: """
+            {
+                "__type": "aws.protocoltests.json10#NoCustomCodeError",
+                "Message": "Hi"
+            }"""
+        bodyMediaType: "application/json"
+        vendorParamsShape: ErrorCodeParams
+        vendorParams: { code: "NoCustomCodeError" }
+    }
+])
+
+@awsQueryError(code: "Customized", httpResponseCode: 402)
+@error("client")
+structure CustomCodeError {
+    message: String
+}
+
+apply CustomCodeError @httpResponseTests([
+    {
+        id: "QueryCompatibleAwsJson10CustomCodeError"
+        documentation: "Parses simple errors with query error code"
+        protocol: awsJson1_0
+        params: { message: "Hi" }
+        code: 400
+        headers: { "Content-Type": "application/x-amz-json-1.0", "x-amzn-query-error": "Customized;Sender" }
+        body: """
+            {
+                "__type": "aws.protocoltests.json10#CustomCodeError",
+                "Message": "Hi"
+            }"""
+        bodyMediaType: "application/json"
+        vendorParamsShape: ErrorCodeParams
+        vendorParams: { code: "Customized", type: "Sender" }
+    }
+])
diff --git a/smithy-aws-protocol-tests/model/awsQuery/xml-errors.smithy b/smithy-aws-protocol-tests/model/awsQuery/xml-errors.smithy
index 3d64b5626..c21141487 100644
--- a/smithy-aws-protocol-tests/model/awsQuery/xml-errors.smithy
+++ b/smithy-aws-protocol-tests/model/awsQuery/xml-errors.smithy
@@ -29,6 +29,7 @@ namespace aws.protocoltests.query
 
 use aws.protocols#awsQueryError
 use aws.protocols#awsQuery
+use aws.protocoltests.config#ErrorCodeParams
 use smithy.test#httpResponseTests
 
 /// This operation has three possible return values:
@@ -97,6 +98,11 @@ apply InvalidGreeting @httpResponseTests([
               </ErrorResponse>
               """,
         bodyMediaType: "application/xml",
+        vendorParamsShape: ErrorCodeParams
+        vendorParams: {
+            code: "InvalidGreeting"
+            type: "Sender"
+        }
     }
 ])
 
@@ -136,6 +142,11 @@ apply ComplexError @httpResponseTests([
               </ErrorResponse>
               """,
         bodyMediaType: "application/xml",
+        vendorParamsShape: ErrorCodeParams
+        vendorParams: {
+            code: "ComplexError"
+            type: "Sender"
+        }
     }
 ])
 
@@ -175,5 +186,10 @@ apply CustomCodeError @httpResponseTests([
               </ErrorResponse>
               """,
         bodyMediaType: "application/xml",
+        vendorParamsShape: ErrorCodeParams
+        vendorParams: {
+            code: "Customized"
+            type: "Sender"
+        }
     }
 ])
diff --git a/smithy-aws-protocol-tests/model/ec2Query/xml-errors.smithy b/smithy-aws-protocol-tests/model/ec2Query/xml-errors.smithy
index fec1cd73b..0dc823790 100644
--- a/smithy-aws-protocol-tests/model/ec2Query/xml-errors.smithy
+++ b/smithy-aws-protocol-tests/model/ec2Query/xml-errors.smithy
@@ -23,6 +23,7 @@ $version: "2.0"
 namespace aws.protocoltests.ec2
 
 use aws.protocols#ec2Query
+use aws.protocoltests.config#ErrorCodeParams
 use smithy.test#httpResponseTests
 
 /// This operation has three possible return values:
@@ -91,6 +92,10 @@ apply InvalidGreeting @httpResponseTests([
         params: {
             Message: "Hi"
         },
+        vendorParamsShape: ErrorCodeParams
+        vendorParams: {
+            code: "InvalidGreeting"
+        }
     }
 ])
 
@@ -132,6 +137,10 @@ apply ComplexError @httpResponseTests([
               </Response>
               """,
         bodyMediaType: "application/xml",
+        vendorParamsShape: ErrorCodeParams
+        vendorParams: {
+            code: "ComplexError"
+        }
     }
 ])
 
diff --git a/smithy-aws-protocol-tests/model/rpcv2Cbor/non-query-compatible.smithy b/smithy-aws-protocol-tests/model/rpcv2Cbor/non-query-compatible.smithy
new file mode 100644
index 000000000..69e029090
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rpcv2Cbor/non-query-compatible.smithy
@@ -0,0 +1,34 @@
+$version: "2"
+
+namespace aws.protocoltests.rpcv2cbor
+
+use aws.api#service
+use aws.auth#sigv4
+use smithy.protocols#rpcv2Cbor
+use smithy.test#httpRequestTests
+
+@service(sdkId: "Non Query Compatible RpcV2 Protocol")
+@sigv4(name: "nonquerycompatiblerpcv2protocol")
+@rpcv2Cbor
+@title("Non Query Compatible RpcV2 Protocol Service")
+service NonQueryCompatibleRpcV2Protocol {
+    version: "2025-06-20"
+    operations: [
+        NonQueryCompatibleOperation
+    ]
+}
+
+@httpRequestTests([
+    {
+        id: "NonQueryCompatibleRpcV2CborForbidsQueryModeHeader"
+        documentation: "The query mode header MUST NOT be set on non-query-compatible services."
+        protocol: rpcv2Cbor
+        method: "POST"
+        headers: { "smithy-protocol": "rpc-v2-cbor", Accept: "application/cbor" }
+        forbidHeaders: ["x-amzn-query-mode"]
+        uri: "/service/NonQueryCompatibleRpcV2Protocol/operation/QueryIncompatibleOperation"
+        body: ""
+    }
+])
+@idempotent
+operation NonQueryCompatibleOperation {}
diff --git a/smithy-aws-protocol-tests/model/rpcv2Cbor/query-compatible.smithy b/smithy-aws-protocol-tests/model/rpcv2Cbor/query-compatible.smithy
new file mode 100644
index 000000000..d0707005a
--- /dev/null
+++ b/smithy-aws-protocol-tests/model/rpcv2Cbor/query-compatible.smithy
@@ -0,0 +1,88 @@
+$version: "2"
+
+namespace aws.protocoltests.rpcv2cbor
+
+use aws.api#service
+use aws.auth#sigv4
+use aws.protocols#awsQueryCompatible
+use aws.protocols#awsQueryError
+use aws.protocoltests.config#ErrorCodeParams
+use smithy.protocols#rpcv2Cbor
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+
+@service(sdkId: "Query Compatible RpcV2 Protocol")
+@sigv4(name: "querycompatiblerpcv2protocol")
+@rpcv2Cbor
+@title("Query Compatible RpcV2 Protocol Service")
+@awsQueryCompatible
+service QueryCompatibleRpcV2Protocol {
+    version: "2025-06-20"
+    operations: [
+        QueryCompatibleOperation
+    ]
+}
+
+
+@idempotent
+operation QueryCompatibleOperation {
+    errors: [
+        NoCustomCodeError
+        CustomCodeError
+    ]
+}
+
+apply QueryCompatibleOperation @httpRequestTests([
+    {
+        id: "QueryCompatibleRpcV2CborSendsQueryModeHeader"
+        documentation: "Clients for query-compatible services MUST send the x-amzn-query-mode header."
+        protocol: rpcv2Cbor
+        method: "POST"
+        headers: { "smithy-protocol": "rpc-v2-cbor", Accept: "application/cbor", "x-amzn-query-mode": "true" }
+        forbidHeaders: ["Content-Type", "X-Amz-Target"]
+        uri: "/service/QueryCompatibleRpcV2Protocol/operation/QueryCompatibleOperation"
+        body: ""
+    }
+])
+
+@error("client")
+structure NoCustomCodeError {
+    message: String
+}
+
+apply NoCustomCodeError @httpResponseTests([
+    {
+        id: "QueryCompatibleRpcV2CborNoCustomCodeError"
+        documentation: "Parses simple RpcV2 CBOR errors with no query error code"
+        protocol: rpcv2Cbor
+        params: { message: "Hi" }
+        code: 400
+        headers: { "smithy-protocol": "rpc-v2-cbor", "Content-Type": "application/cbor" }
+        body: "uQACZl9fdHlwZXgtYXdzLnByb3RvY29sdGVzdHMucnBjdjJjYm9yI05vQ3VzdG9tQ29kZUVycm9yZ01lc3NhZ2ViSGk="
+        bodyMediaType: "application/cbor"
+        vendorParamsShape: ErrorCodeParams
+        vendorParams: { code: "NoCustomCodeError" }
+    }
+])
+
+@awsQueryError(code: "Customized", httpResponseCode: 402)
+@error("client")
+structure CustomCodeError {
+    message: String
+}
+
+apply CustomCodeError @httpResponseTests([
+    {
+        id: "QueryCompatibleRpcV2CborCustomCodeError"
+        documentation: "Parses simple RpcV2 CBOR errors with query error code"
+        protocol: rpcv2Cbor
+        params: { message: "Hi" }
+        code: 400
+        headers: { "smithy-protocol": "rpc-v2-cbor", "Content-Type": "application/cbor", "x-amzn-query-error": "Customized;Sender" }
+        body: "uQACZl9fdHlwZXgrYXdzLnByb3RvY29sdGVzdHMucnBjdjJjYm9yI0N1c3RvbUNvZGVFcnJvcmdNZXNzYWdlYkhp"
+        bodyMediaType: "application/cbor"
+        vendorParamsShape: ErrorCodeParams
+        vendorParams: { code: "Customized", type: "Sender" }
+    }
+])
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/protocols/query-errors-must-be-unique.errors b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/protocols/query-errors-must-be-unique.errors
new file mode 100644
index 000000000..19809b6d2
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/protocols/query-errors-must-be-unique.errors
@@ -0,0 +1,2 @@
+[WARNING] smithy.example#QueryCompatibleService: Multiple error shapes with the error code `ResourceNotFound` found: [smithy.example#DocumentNotFound, smithy.example#ResourceNotFound]. This error code ambiguity will result in the wrong error shape being deserialized when running the query protocol. This pain will be carried forward when migrating to a new protocol when using the awsQueryCompatible trait. | QueryErrorCode
+[WARNING] smithy.example#QueryService: Multiple error shapes with the error code `ResourceNotFound` found: [smithy.example#DocumentNotFound, smithy.example#ResourceNotFound]. This error code ambiguity will result in the wrong error shape being deserialized when running the query protocol. This pain will be carried forward when migrating to a new protocol when using the awsQueryCompatible trait. | QueryErrorCode
diff --git a/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/protocols/query-errors-must-be-unique.smithy b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/protocols/query-errors-must-be-unique.smithy
new file mode 100644
index 000000000..c6052402e
--- /dev/null
+++ b/smithy-aws-traits/src/test/resources/software/amazon/smithy/aws/traits/errorfiles/protocols/query-errors-must-be-unique.smithy
@@ -0,0 +1,39 @@
+$version: "2.0"
+
+namespace smithy.example
+
+use aws.protocols#awsJson1_0
+use aws.protocols#awsQuery
+use aws.protocols#awsQueryCompatible
+use aws.protocols#awsQueryError
+
+@awsQueryCompatible
+@awsJson1_0
+service QueryCompatibleService {
+    version: "2020-02-05"
+    errors: [
+        DocumentNotFound
+        ResourceNotFound
+    ]
+}
+
+@awsQueryError(code: "ResourceNotFound", httpResponseCode: 400)
+@error("client")
+structure DocumentNotFound {
+    message: String
+}
+
+@error("client")
+structure ResourceNotFound {
+    message: String
+}
+
+@awsQuery
+@xmlNamespace(uri: "https://example.com/")
+service QueryService {
+    version: "2020-02-05"
+    errors: [
+        DocumentNotFound
+        ResourceNotFound
+    ]
+}
diff --git a/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/protocols/examples-test.openapi.json b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/protocols/examples-test.openapi.json
index 4e2350e58..9d4d7413f 100644
--- a/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/protocols/examples-test.openapi.json
+++ b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/protocols/examples-test.openapi.json
@@ -149,7 +149,7 @@
                     "description": "withdrawTestDoc",
                     "value": {
                       "location": "Denver",
-                      "bankName": "Chase",
+                      "bank": "Chase",
                       "atmRecording": "dGVzdHZpZGVv"
                     }
                   }
@@ -470,7 +470,7 @@
           "location": {
             "type": "string"
           },
-          "bankName": {
+          "bank": {
             "type": "string"
           },
           "atmRecording": {
diff --git a/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/protocols/examples-test.smithy b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/protocols/examples-test.smithy
index b32459f4b..5bd61b88a 100644
--- a/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/protocols/examples-test.smithy
+++ b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/protocols/examples-test.smithy
@@ -1,59 +1,92 @@
+$version: "2"
+
 namespace smithy.examplestrait
 
 use aws.protocols#restJson1
 
 @restJson1
 service Banking {
-    version: "2022-06-26",
-    operations: [Deposit, Withdraw]
+    version: "2022-06-26"
+    operations: [
+        Deposit
+        Withdraw
+    ]
 }
 
 @idempotent
 @http(method: "PUT", uri: "/account/{username}", code: 200)
 operation Deposit {
-    input: DepositInput,
-    output: DepositOutput,
-    errors: [InvalidUsername, InvalidAmount]
+    input := {
+        @httpHeader("accountNumber")
+        accountNumber: String
+
+        @required
+        @httpLabel
+        username: String
+
+        @httpQuery("accountHistory")
+        accountHistory: ExampleList
+
+        @httpPayload
+        depositAmount: String
+    }
+
+    output := {
+        @httpHeader("username")
+        username: String
+
+        @httpHeader("authenticationResult")
+        authenticationResult: ExampleList
+
+        textMessage: String
+
+        emailMessage: String
+    }
+
+    errors: [
+        InvalidUsername
+        InvalidAmount
+    ]
 }
 
 @idempotent
 @http(method: "PATCH", uri: "/account/withdraw", code: 200)
 operation Withdraw {
-    input: WithdrawInput,
-    output: WithdrawOutput,
-    errors: [InvalidUsername]
-}
+    input := {
+        @httpHeader("accountNumber")
+        accountNumber: String
 
-@input
-structure DepositInput {
-    @httpHeader("accountNumber")
-    accountNumber: String,
+        @httpHeader("username")
+        username: String
 
-    @required
-    @httpLabel
-    username: String,
+        @httpQueryParams
+        withdrawParams: ExampleMap
 
-    @httpQuery("accountHistory")
-    accountHistory: ExampleList,
+        time: date
 
-    @httpPayload
-    depositAmount: String
-}
+        withdrawAmount: String
 
-@input
-structure WithdrawInput {
-    @httpHeader("accountNumber")
-    accountNumber: String,
+        withdrawOption: String
+    }
 
-    @httpHeader("username")
-    username: String,
+    output := {
+        @httpHeader("branch")
+        branch: String
 
-    @httpQueryParams()
-    withdrawParams: ExampleMap,
+        @httpHeader("result")
+        accountHistory: ExampleList
 
-    time: date,
-    withdrawAmount: String,
-    withdrawOption: String
+        location: String
+
+        @jsonName("bank")
+        bankName: String
+
+        atmRecording: exampleVideo
+    }
+
+    errors: [
+        InvalidUsername
+    ]
 }
 
 list ExampleList {
@@ -61,7 +94,7 @@ list ExampleList {
 }
 
 map ExampleMap {
-    key: String,
+    key: String
     value: String
 }
 
@@ -71,35 +104,10 @@ blob exampleVideo
 @timestampFormat("http-date")
 timestamp date
 
-@output
-structure DepositOutput {
-    @httpHeader("username")
-    username: String,
-
-    @httpHeader("authenticationResult")
-    authenticationResult: ExampleList,
-
-    textMessage: String,
-    emailMessage: String
-}
-
-@output
-structure WithdrawOutput {
-    @httpHeader("branch")
-    branch: String,
-
-    @httpHeader("result")
-    accountHistory: ExampleList,
-
-    location: String,
-    bankName: String,
-    atmRecording: exampleVideo
-}
-
 @error("client")
 structure InvalidUsername {
     @httpHeader("internalErrorCode")
-    internalErrorCode: String,
+    internalErrorCode: String
 
     @httpPayload
     errorMessage: String
@@ -107,107 +115,90 @@ structure InvalidUsername {
 
 @error("server")
 structure InvalidAmount {
-    errorMessage1: String,
-    errorMessage2: String,
+    errorMessage1: String
+    errorMessage2: String
     errorMessage3: String
 }
 
-apply Deposit @examples(
-    [
-        {
-            title: "Deposit valid example",
-            documentation: "depositTestDoc",
-            input: {
-                accountNumber: "102935",
-                username: "sichanyoo",
-                accountHistory: ["10", "-25", "50"],
-                depositAmount: "200"
-            },
-            output: {
-                username: "sichanyoo",
-                authenticationResult: ["pass1", "pass2", "pass3"],
-                textMessage: "You deposited 200-text",
-                emailMessage: "You deposited 200-email"
-            },
-        },
-
-        {
-            title: "Deposit invalid username example",
-            documentation: "depositTestDoc2",
-            input: {
-                username: "sichanyoo",
-                accountHistory: ["-200", "200", "10"],
-                depositAmount: "-200"
-            },
-            error: {
-                shapeId: InvalidUsername,
-                content: {
-                    internalErrorCode: "4gsw2-34",
-                    errorMessage: "ERROR: Invalid username."
-                }
-            },
-        },
-
-        {
-            title: "Deposit invalid amount example",
-            documentation: "depositTestDoc3",
-            input: {
-                accountNumber: "203952",
-                username: "obidos",
-                accountHistory: ["2000", "50000", "100"],
-                depositAmount: "-100"
-            },
-            error: {
-                shapeId: InvalidAmount,
-                content: {
-                    errorMessage1: "ERROR: Invalid amount.",
-                    errorMessage2: "2gdx4-34",
-                    errorMessage3: "2gcbe-98"
-                }
-            },
+apply Deposit @examples([
+    {
+        title: "Deposit valid example"
+        documentation: "depositTestDoc"
+        input: {
+            accountNumber: "102935"
+            username: "sichanyoo"
+            accountHistory: ["10", "-25", "50"]
+            depositAmount: "200"
         }
-    ]
-)
-
-apply Withdraw @examples(
-    [
-        {
-            title: "Withdraw valid example",
-            documentation: "withdrawTestDoc",
-            input: {
-                accountNumber: "124634",
-                username: "amazon",
-                withdrawParams: {"location" : "Denver", "bankName" : "Chase"},
-                time: "Tue, 29 Apr 2014 18:30:38 GMT",
-                withdrawAmount: "-35",
-                withdrawOption: "ATM"
-            },
-            output: {
-                branch: "Denver-203",
-                accountHistory: ["34", "5", "-250"],
-                location: "Denver",
-                bankName: "Chase",
-                atmRecording: "dGVzdHZpZGVv"
-            },
-        },
-
-        {
-            title: "Withdraw invalid username example",
-            documentation: "withdrawTestDoc2",
-            input: {
-                accountNumber: "231565",
-                username: "peccy",
-                withdrawParams: {"location" : "Seoul", "bankName" : "Chase"},
-                withdrawAmount: "-450",
-                withdrawOption: "Venmo"
-            },
-            error: {
-                shapeId: InvalidUsername,
-                content: {
-                    internalErrorCode: "8dfws-21",
-                    errorMessage: "ERROR: Invalid username."
-                }
-            },
+        output: {
+            username: "sichanyoo"
+            authenticationResult: ["pass1", "pass2", "pass3"]
+            textMessage: "You deposited 200-text"
+            emailMessage: "You deposited 200-email"
         }
-    ]
-)
+    }
+    {
+        title: "Deposit invalid username example"
+        documentation: "depositTestDoc2"
+        input: {
+            username: "sichanyoo"
+            accountHistory: ["-200", "200", "10"]
+            depositAmount: "-200"
+        }
+        error: {
+            shapeId: InvalidUsername
+            content: { internalErrorCode: "4gsw2-34", errorMessage: "ERROR: Invalid username." }
+        }
+    }
+    {
+        title: "Deposit invalid amount example"
+        documentation: "depositTestDoc3"
+        input: {
+            accountNumber: "203952"
+            username: "obidos"
+            accountHistory: ["2000", "50000", "100"]
+            depositAmount: "-100"
+        }
+        error: {
+            shapeId: InvalidAmount
+            content: { errorMessage1: "ERROR: Invalid amount.", errorMessage2: "2gdx4-34", errorMessage3: "2gcbe-98" }
+        }
+    }
+])
+
+apply Withdraw @examples([
+    {
+        title: "Withdraw valid example"
+        documentation: "withdrawTestDoc"
+        input: {
+            accountNumber: "124634"
+            username: "amazon"
+            withdrawParams: { location: "Denver", bankName: "Chase" }
+            time: "Tue, 29 Apr 2014 18:30:38 GMT"
+            withdrawAmount: "-35"
+            withdrawOption: "ATM"
+        }
+        output: {
+            branch: "Denver-203"
+            accountHistory: ["34", "5", "-250"]
+            location: "Denver"
+            bankName: "Chase"
+            atmRecording: "dGVzdHZpZGVv"
+        }
+    }
+    {
+        title: "Withdraw invalid username example"
+        documentation: "withdrawTestDoc2"
+        input: {
+            accountNumber: "231565"
+            username: "peccy"
+            withdrawParams: { location: "Seoul", bankName: "Chase" }
+            withdrawAmount: "-450"
+            withdrawOption: "Venmo"
+        }
+        error: {
+            shapeId: InvalidUsername
+            content: { internalErrorCode: "8dfws-21", errorMessage: "ERROR: Invalid username." }
+        }
+    }
+])