Skip to main content

type-mapping

Rules for converting Smithy shape types to .NET types, including nullability and collection defaults

Source facts

Repository
aws/aws-sdk-net
Last source activity
August 12, 2026 at 16:26
Detected SKILL.md language
English
Stars
131
Forks
890

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
type-mapping
description
Rules for converting Smithy shape types to .NET types, including nullability and collection defaults
# Skill: Smithy to .NET Type Mapping ## Purpose Definitive mapping from Smithy shape types to .NET types, plus nullability and collection default rules. ## Type Mapping Table | Smithy shape | .NET type | Notes | |---|---|---| | `boolean` | `bool?` | Nullable | | `string` | `string` | Reference type, nullable by nature | | `integer` | `int?` | Nullable | | `long` | `long?` | Nullable | | `float` | `float?` | Nullable | | `double` | `double?` | Nullable | | `timestamp` | `DateTime?` | Nullable | | `byte` | — | Not supported yet — throws. No settled .NET mapping (the current SDK never emitted `byte`/`short`) | | `short` | — | Not supported yet — throws | | `bigInteger` | — | Not supported yet — throws. Wider-numeric types are earmarked for a dedicated numerics extension | | `bigDecimal` | — | Not supported yet — throws | | `blob` | `MemoryStream` | Not supported yet — throws. Target: streaming blobs → `Stream`, non-streaming → `MemoryStream` | | `document` | `Amazon.Runtime.Documents.Document` | Not supported yet — throws. SDK runtime type | | `enum` | `string` (Phase 1) / `ConstantClass` (Phase 2) | Not supported yet — throws. Phase 2 uses the `ConstantClass` pattern | | `intEnum` | `int?` | Not supported yet — throws | | `list` | `List<T>` | V4 default: `null`; see Collection Defaults | | `map` | `Dictionary<TKey, TValue>` | V4 default: `null`; see Collection Defaults | | `structure` | Generated class | See structure rules below | | `union` | Generated class | Generated as regular structure (matches current SDK) | ## Nullability Rules The AWS SDK for .NET V4 convention: 1. **All value types are nullable** — `bool?`, `int?`, `DateTime?`, etc. regardless of `@required` or `@default` traits 2. **Reference types** (`string`, `MemoryStream`) — nullable by nature, no `?` suffix needed in the type declaration 3. **Collections** — V4 default: `null`; see Collection Defaults section 4. **The `@default` trait does NOT change nullability** — this matches current SDK behavior 5. **The `@required` trait does NOT change nullability** — it only affects the `[AWSProperty(Required=true)]` attribute ### Opt-in Strict Nullability (Non-AWS Use) The generator will support an opt-in mode that respects Smithy's nullability traits (`@required`, `@default`, `@clientOptional`) for non-AWS Smithy models. When disabled (the default for AWS), all value types are nullable regardless of traits. ## Collection Defaults Collections use `AWSConfigs.InitializeCollections` for SDK V4 backwards compatibility. The generator emits an auto-property with the initializer expression directly, plus an internal `IsSet{Property}()` method that the AWS SDK runtime (and marshallers) call: ```csharp public List<AuditEvent> AuditEvents { get; set; } = AWSConfigs.InitializeCollections ? new List<AuditEvent>() : null; internal bool IsSetAuditEvents() => this.AuditEvents != null && (this.AuditEvents.Count > 0 || !AWSConfigs.InitializeCollections); ``` When `AWSConfigs.InitializeCollections` is `false` (V4 default), collections start as `null`, and an empty list still counts as "set" (the caller cleared the value). When `true` (V3 compat), collections start empty and an empty list counts as "not set". The `IsSet` method encodes that rule so callers — including the public reflection API `AWSSDKUtils.IsPropertySet` — see the correct answer in both modes. ## Constrained Shapes Smithy allows constrained shapes (e.g. a `string` shape named `Uuid` with `@length` or `@pattern`). These do NOT get wrapper classes — they map to their underlying .NET type: | Smithy | .NET | |---|---| | `Uuid` (string shape with constraints) | `string` | | `AuditEvents` (list shape) | `List<AuditEvent>` inline | | `ChannelArn` (string shape) | `string` | The constraints flow through to `[AWSProperty]` attributes on the **member that references the shape**. When resolving `[AWSProperty]`, check traits on **both** the member and its target shape: - `@required` is on the member - `@length` / `@range` are typically on the target shape (e.g. `AuditEvents` list has `@length(min:1, max:100)`, so `PutAuditEventsRequest.AuditEvents` gets `[AWSProperty(Required=true, Min=1, Max=100)]`) ## Error Shape Naming Smithy error shapes often omit the `Exception` suffix. The naming rules (matching the existing generator's `ExceptionShape.Name` logic): - Ends with `Exception` → keep as-is (e.g. `UnsupportedOperationException`) - Ends with `Fault` → replace `Fault` with `Exception` - Otherwise → append `Exception` (e.g. `ChannelNotFound` → `ChannelNotFoundException`) ## Error Shape Members Error shapes have a `message` member in the Smithy model, but the generated exception class does **not** expose it as a property. The `message` is passed to `System.Exception` via the constructor and inherited as `Exception.Message`. The generator must filter out the `message` member when generating exception properties. ## Resolving Member Types To get the .NET type for a structure member: 1. Get the member's `Target` shape ID 2. `GenerationContext.Resolve(target)` returns the shape. Prelude shapes (`smithy.api#String`, etc.) are not in the model's shape map, but `Resolve` falls back to `PreludeShapes`, so callers never special-case them — a prelude `String` comes back as a `StringShape` like any other. Map the resolved shape's `type` from the table: - Simple/scalar shape → map its `type` from the table - Structure/union → use the generated class name - List → `List<{resolve member.Target}>` - Map → `Dictionary<{resolve key.Target}, {resolve value.Target}>` - Enum → `string` (Phase 1), `ConstantClass` subclass (Phase 2) - Constrained string shapes (e.g. `Uuid`) resolve to a `StringShape` → `string` (no wrapper) ## Prelude Shape Mapping These shapes are implicit (not in the model JSON) and map directly. The .NET types below are the target mapping; see the Type Mapping Table above for which are supported today vs. still throw (`Blob`/`Document` are not supported yet): | Prelude shape ID | .NET type | |---|---| | `smithy.api#String` | `string` | | `smithy.api#Boolean` | `bool?` | | `smithy.api#Integer` | `int?` | | `smithy.api#Long` | `long?` | | `smithy.api#Float` | `float?` | | `smithy.api#Double` | `double?` | | `smithy.api#Blob` | `MemoryStream` | | `smithy.api#Timestamp` | `DateTime?` | | `smithy.api#Document` | `Amazon.Runtime.Documents.Document` | | `smithy.api#Unit` | (no type — used for operations with no input/output) |
View on GitHub