Skip to content

Shape Mapping

Smithy typeC# type
blobbyte[]
booleanbool
stringstring
bytesbyte
shortshort
integerint
longlong
floatfloat
doubledouble
bigIntegerSystem.Numerics.BigInteger
bigDecimaldecimal
timestampDateTimeOffset
documentNSmithy.Core.Document

Optional members (those without @required) are generated as their nullable counterpart (e.g. string?, int?).

Smithy string enums are generated as a C# readonly record struct wrapping a string. The known values are exposed as public static readonly constants on the type.

Using a record struct rather than a System.Enum subclass preserves forward compatibility: an API response can carry an unknown enum value and the client code compiles and runs without change.

// Smithy: enum Status { ACTIVE INACTIVE }
public readonly record struct Status(string Value)
{
public static readonly Status Active = new("ACTIVE");
public static readonly Status Inactive = new("INACTIVE");
}

Smithy integer enums are generated as a C# readonly record struct wrapping an int, following the same forward-compatibility reasoning as string enums.

// Smithy: intEnum Priority { LOW = 1, HIGH = 2 }
public readonly record struct Priority(int Value)
{
public static readonly Priority Low = new(1);
public static readonly Priority High = new(2);
}

Structures are generated as C# record types with positional constructor parameters for required members and optional (nullable) properties for non-required members. Required members that carry a default value in the Smithy model are generated with that default.

// Smithy:
// structure GetWidgetInput {
// @required @httpLabel id: String
// filter: String
// }
public record GetWidgetInput(string Id, string? Filter = null);

Each generated structure implements ISerializableShape and IDeserializableShape (see Serialization) and carries a static readonly Schema field.

Smithy error shapes are generated as C# Exception subclasses. The generated class extends a service-specific base error type, which in turn extends SmithyException from NSmithy.Core.

// Smithy: @error("client") structure NotFoundException { message: String }
public sealed class NotFoundException : HelloServiceException
{
public string? Message { get; }
// ...
}

The @fault value ("client" or "server") and the error code (shape name) are accessible as properties on the exception.

Smithy unions are generated as a sealed C# class hierarchy. A sealed abstract base class represents the union type; each member becomes a concrete nested record subclass holding the member value.

// Smithy: union Shape { circle: Circle, square: Square }
public abstract record Shape
{
public sealed record CircleCase(Circle Value) : Shape;
public sealed record SquareCase(Square Value) : Shape;
}

This enables exhaustiveness checks via switch expressions in C# 8+.

Smithy list shapes are generated as IReadOnlyList<T> at usage sites. There is no standalone generated class for a list shape; the element type is resolved recursively and the list is represented inline.

Smithy map shapes are generated as IReadOnlyDictionary<TKey, TValue>. Keys must be string or a string-like type.

A Smithy service shape produces two generated files:

  • <Service>Client.g.cs — a typed async client class with one method per operation.
  • <Service>Server.g.cs — a handler interface (I<Service>Handler) and an ASP.NET Core adapter (<Service>Server).

Smithy namespace segments are capitalised to PascalCase and joined with . to form a C# namespace. If baseNamespace is set in smithy-build.json, it is prepended.

Examples (with empty baseNamespace):

Smithy namespaceC# namespace
example.helloExample.Hello
com.example.widgetsCom.Example.Widgets
  • Type names follow PascalCase (GetWidgetInput, NotFoundException).
  • Member names follow PascalCase (FirstName, CreatedAt).
  • Smithy camelCase member names are converted to PascalCase in C#.
  • Smithy names that conflict with C# keywords are escaped with a @ prefix.