Official agent skill

Marshalling

by aws in aws/aws-sdk-net

What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol.

OfficialApache-2.0Auto-check passed

Install Marshalling

skills CLI
$ npx skills add aws/aws-sdk-net --skill marshalling -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install aws/aws-sdk-net marshalling --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/aws/aws-sdk-net.git skills-src && mkdir -p .claude/skills && cp -r skills-src/generator/SmithyDotNet/skills/marshalling .claude/skills/marshalling && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
marshalling
GitHub stars
145
Token cost
~8.2k tokens
SKILL.md length
3,425 words
Files
1
Skills in repo
6
Repo updated
First seen
Licence
Apache-2.0

At a glance

What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol.

  • Works in 6 steps: new DefaultRequest(publicRequest,… → Content-Type (protocol-dependent, or the… → HeaderKeys.XAmzApiVersion (the service… → …
  • Reviewing any marshaller/unmarshaller writer
  • SKILL.md covers File Layout, Request Marshaller Scaffolding, Member Placement and Wire Name Resolution, plus 10 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Marshalling is an agent skill from aws/aws-sdk-net, published by the product's own GitHub organization. What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol. Use when writing or reviewing any marshaller/unmarshaller writer.

Its SKILL.md is about 8.2k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

The repository describes itself as: The official AWS SDK for .NET. For more information on the AWS SDK for .NET, see our web site:. The licence is Apache-2.0.

When your agent uses it

  • Reviewing any marshaller/unmarshaller writer

Example prompts

  • “/marshalling”

Workflow steps

6 steps, taken from the first numbered list in SKILL.md.

  1. new DefaultRequest(publicRequest, "Amazon.{ServiceName}"), then @requestCompression (below).
  2. Content-Type (protocol-dependent, or the overrideContentType customization when set — below),
  3. HeaderKeys.XAmzApiVersion (the service shape's version) and HttpMethod.
  4. @httpQuery members, then @httpQueryParams; @httpPrefixHeaders, then @httpHeader members.
  5. @httpLabel members as AddPathResource calls, then request.ResourcePath set to the @http uri
  6. The body (below), @httpChecksumRequired, @unsignedPayload, UseQueryString, @endpoint host

What it can do on your machine

Read from SKILL.md and the folder at commit f36df89. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md.

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • smithy.io

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Marshalling loads about 8.2k tokens when it runs. Until then it costs about 54 tokens; SKILL.md has 3,425 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~54
When it runs · the whole SKILL.md, loaded when a task matches
~8.2k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from aws/aws-sdk-net at commit f36df89, republished under its Apache-2.0 licence (© aws). 3,425 words, ~8,192 tokens.

Download SKILL.mdSave it as .claude/skills/marshalling/SKILL.md (or your agent's skills folder).
name
marshalling
description
What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol. Use when writing or reviewing any marshaller/unmarshaller writer.

Skill: Marshalling

This skill states the emitted output. Where a rule exists only for parity with the legacy C2J generator, it says so; everything else follows from the Smithy spec.

File Layout

Under Generated/Model/Internal/MarshallTransformations/, all partial:

FileClassNotes
{Operation}RequestMarshaller.csIMarshaller<IRequest, {Operation}Request>
{Operation}ResponseUnmarshaller.csJsonResponseUnmarshaller (or protocol equivalent)Dispatches errors
{Shape}Marshaller.csIRequestMarshaller<{Shape}, JsonMarshallerContext> (or protocol equivalent)Nested structures in the request path
{Shape}Unmarshaller.csIJsonUnmarshaller<{Shape}, JsonUnmarshallerContext> (or protocol equivalent)Nested structures in the response path
{Exception}Unmarshaller.csIJsonErrorResponseUnmarshaller<{Exception}, JsonUnmarshallerContext> (or protocol equivalent)
{Operation}EndpointDiscoveryMarshaller.csIMarshaller<EndpointDiscoveryDataBase, {Operation}Request>Endpoint discovery only; see sdk-conventions

An operation output already named {Operation}Response that a member also targets is typed with the {Operation}Response class itself (no separate model class); its structure unmarshaller is {Shape}StructureUnmarshaller, since {Shape}Unmarshaller is the operation's response unmarshaller. No C2J service has this shape, so there is no parity target.

Structure (un)marshaller class names come only from GenerationContext.StructureMarshallerName / StructureUnmarshallerName. TypeDescriptor.MarshallerName / UnmarshallerName carry them for a structure target; writers read those (or the context, when declaring the class or naming the file) and never compose {Shape}Marshaller / {Shape}Unmarshaller themselves, so a naming rule needs no per-protocol code.

Structure marshallers expose public readonly static {Shape}Marshaller Instance = new {Shape}Marshaller();. Operation marshallers/unmarshallers expose a private static instance behind a public Instance property.

Request Marshaller Scaffolding

Every request marshaller emits, in this order:

  1. new DefaultRequest(publicRequest, "Amazon.{ServiceName}"), then @requestCompression (below).
  2. Content-Type (protocol-dependent, or the overrideContentType customization when set — below), omitted for GET/DELETE and for operations with no body.
  3. HeaderKeys.XAmzApiVersion (the service shape's version) and HttpMethod.
  4. @httpQuery members, then @httpQueryParams; @httpPrefixHeaders, then @httpHeader members.
  5. @httpLabel members as AddPathResource calls, then request.ResourcePath set to the @http uri template (labels left as {name} for the runtime to substitute).
  6. The body (below), @httpChecksumRequired, @unsignedPayload, UseQueryString, @endpoint host prefix, return request.

An operation that requires HTTP/2 pins request.HttpProtocolVersion = System.Net.HttpVersion.Version20 (under #if NET8_0_OR_GREATER) right after the DefaultRequest. Which operations require it comes from the protocol trait's version lists, where eventStreamHttp defaults to http when absent or empty:

httpeventStreamHttpOperations pinned to h2
no h2anynone
h2 without http/1.1anyall
h2 and http/1.1without http/1.1those with an output event stream
h2 and http/1.1with http/1.1those with both an input and an output event stream
overrideContentType customization

The service-level overrideContentType customization (a top-level string in *.customizations.json, read into GenerationContext.Customizations.OverrideContentType) hard-codes the request Content-Type: request.Headers["Content-Type"] = "{value}";. On restJson1 it replaces the per-operation value (including the blob-payload default) but is still skipped for input event streams and GET/DELETE; it's emitted even for body-less operations. On awsJson it replaces only the versioned Content-Type; X-Amz-Target is unchanged. A handful of restJson1 services use it to send application/x-amz-json-1.1 instead of the default application/json (finspace, finspace-data, lex.v2), matching C2J.

Member Placement

Smithy traitWhereSDK pattern
@httpQuery("name") scalarQuery stringrequest.Parameters.Add("name", StringUtils.FromString(...))
@httpQuery("name") list<string>Query stringrequest.ParameterCollection.Add("name", publicRequest.Prop) (repeated params, ordinal-sorted at runtime)
@httpQuery("name") list<value-type>Query stringrequest.ParameterCollection.Add("name", publicRequest.Prop.ConvertAll<string>(item => StringUtils.FromX(item)))
@httpQueryParams mapQuery stringLoop entries into query params (see below); @httpQuery wins on key collision
@httpLabelURI segmentif (!publicRequest.IsSetProp()) throw new Amazon{BaseName}Exception(...), then request.AddPathResource("{name}", <conversion>). A greedy label keeps {name+} as the key and passes the value through .TrimStart('/')
@httpHeader("name") scalarHeaderrequest.Headers["name"] = ...
@httpHeader("name") @mediaType stringHeaderBase64: Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(...)); the read side decodes (C2J "jsonvalue"). Body-bound @mediaType strings are plain
@httpHeader("name") list<string>Headerrequest.Headers["name"] = StringUtils.FromList(publicRequest.Prop) (comma join, RFC-7230 quoting)
@httpHeader("name") list<value-type>Headerrequest.Headers["name"] = StringUtils.FromValueTypeList(publicRequest.Prop) (the List<T> overload lowercases bool and forces DateTime to RFC822, so no per-element or per-format branch is emitted)
@httpPrefixHeaders("prefix") mapMultiple headersLoop map<string,string>, emit {prefix}{key} headers (see below); request & response
@httpPayloadEntire bodyDirect stream/string (skips body serialization)
@hostLabelEndpoint host prefixrequest.HostPrefix label in addition to the member's normal binding (see below)
@httpResponseCode(response only)unmarshalledObject.{Prop} = (int)context.ResponseData.StatusCode; (see below)
No HTTP traitBodyProtocol-specific serialization

Every query/header/label member is IsSet-guarded. A @required query member (an idempotency token excepted) first throws Amazon{BaseName}Exception("Request object does not have required field {Prop} set") when unset: string.IsNullOrEmpty for a string/enum, == null for anything else. Body members get no required check.

List query/header bindings accept list<string>/list<enum> (an enum element is a plain string, see type-mapping) and value-type element lists (int, long, bool, double, float, timestamp, intEnum), matching C2J. A non-scalar element (blob, structure, nested list/map) fails loud in both positions, as does a @sparse value-type list (a sparse string/enum list is accepted). A header list<timestamp> is only supported with the default/http-date format (the runtime helper always emits RFC822).

A single member may carry @httpQueryParams: a map<string, string> or map<string, list<string>> whose entries each become query params under the @httpQuery rules (a list<string> value repeats the key via request.ParameterCollection; a map<string, string> adds StringUtils.FromString(kvp.Value) via request.Parameters). It's emitted after the explicit @httpQuery members, each entry guarded by a ContainsKey check that skips keys already set, so an explicit @httpQuery wins the collision per the Smithy precedence rule. The guard intentionally does NOT cover query literals in the @http uri (those live in request.SubResources), matching C2J so migration doesn't change wire behavior. The map is request-only and never enters the body; its presence sets request.UseQueryString = true.

A single member may carry @httpPrefixHeaders: a map<string, string> whose entries each become a header {prefix}{key} (an IsSet guard, then a foreach assigning request.Headers[$"{prefix}{kvp.Key}"] = kvp.Value;). It's emitted before the explicit @httpHeader members, so a colliding name (only possible with an empty prefix) is overwritten by the later @httpHeader assignment, per the Smithy precedence rule. Valid on request, response and error structures. The map value must be a string (anything else fails loud).

Wire Name Resolution

@jsonName if present, else the Smithy member name verbatim. awsJson1.x ignores @jsonName (see below).

Request Body Serialization

JSON (restJson1, awsJson1.x)

When any member is body-bound, or the @httpPayload is a structure or document, the marshaller writes a Utf8JsonWriter body over a PooledContentStream (#if NETFRAMEWORK: a MemoryStream copied to request.Content). Each body member is IsSet-guarded, then written per Type → Marshal/Unmarshal under its wire name.

  • Structures dispatch to {Shape}Marshaller.Instance; lists/maps loop and recurse to any depth. Map keys are always strings.
  • Collection leaves: string, value-type scalar, structure, document, or non-streaming blob. Non-sparse value-type leaves are non-nullable (see type-mapping), so they write bare: no .Value unwrap and no float/double NaN guard (both are member-only). @timestampFormat is honored on leaves.
  • @sparse leaves are nullable and JSON nulls are written, matching C2J. A sparse list null-guards its value-type and blob elements (a null string/structure/document already serializes as null); a sparse map null-guards every value kind.
  • Float/double members branch through StringUtils.IsSpecial{Float,Double}Value so NaN/±Infinity serialize as strings.

Structure marshallers loop the structure's own members with the same rules. A request event-stream event is the one exception: the publisher marshaller calls {Event}Marshaller with the JSON object already open, then reads context.Request.EventHeaders and context.Request.Content. Event members route by trait:

  • @eventHeader → IsSet-guarded EventStreamHeader("{memberName}") (never @jsonName) with the typed setter for the target shape (SetString string/enum, SetBool/SetInt32/SetInt64/SetTimestamp with .Value, SetByteBuf(….ToArray()) blob), added to EventHeaders. Any other target fails loud.
  • @eventPayload → IsSet-guarded: blob Request.Content = ….ToArray(), string/enum Encoding.UTF8.GetBytes(…) (publisher sends octet-stream / text/plain, C2J parity); structure/union {Type}Marshaller.Instance.Marshall(…, context) into the open object (the protocol tests expect the bare structure as the payload, not one wrapped under the member name). List/map fail loud.
  • Everything else is an ordinary body member (the implicit payload).
@httpPayload (request)

A @httpPayload member IS the entire body: no wrapping object/property name, no other member in the body (Smithy: all others are header/query/label). No IsSet guard (matches C2J), except string/enum.

  • String (enum too) → text/plain (or the target's @mediaType value when present), no writer scaffold: if (publicRequest.IsSet{Prop}()) { request.Content = System.Text.Encoding.UTF8.GetBytes(publicRequest.{Prop}); }. The guard diverges from C2J, whose unguarded GetBytes throws on an unset optional payload (DOTNET-8852).

  • Structure (a union too) → application/json; the scaffold, then the target's marshaller as the body object (WriteStartObject → {Type}Marshaller.Instance.Marshall(publicRequest.{Prop}, context) → WriteEndObject).

  • Document → application/json; the scaffold, then Amazon.Runtime.Documents.Internal.Transform.DocumentMarshaller.Instance.Write(writer, publicRequest.{Prop}); with NO WriteStartObject/WriteEndObject wrapping (the document is the whole JSON value). No C2J precedent.

  • Blob (MemoryStream, or Stream when @streaming) → application/octet-stream (or the target's @mediaType value; overrides the top application/json). Always assigns request.ContentStream = publicRequest.{Prop} ?? new MemoryStream(); first and ends with the Content-Type override, except when the input also has an @httpHeader("Content-Type") member: that header is emitted before the blob block and must win, so the blob's type moves to the top Content-Type line and the trailing override is dropped (restJson1 TestPayloadBlob). The Content-Length handling in between branches on the operation's aws.auth#unsignedPayload and the blob's @requiresLength (C2J parity):

    • @streaming + @unsignedPayload, no @requiresLength → seek to start and set Content-Length when the stream is seekable, else Transfer-Encoding: chunked.
    • @streaming + @requiresLength → the stream MUST be seekable (throws InvalidOperationException otherwise), then always sets Content-Length. @requiresLength wins over the unsigned chunked path.
    • otherwise → seek when seekable, always set Content-Length (no chunked).

    Separately, aws.auth#unsignedPayload on the operation emits request.DisablePayloadSigning = true; after the body block, for any body kind.

List and map payloads fail loud.

@requestCompression and @httpChecksumRequired (request)

Both are one emitted call to a runtime helper in Amazon.Runtime.Internal.Util, at opposite ends of the body.

  • @requestCompression → CompressionAlgorithmUtils.SetCompressionAlgorithm(request, CompressionEncodingAlgorithm.{encoding}); right after new DefaultRequest(...). encodings is a preference list: the first supported entry wins and unsupported ones are skipped (["br", "gzip"] emits gzip); gzip is the whole supported set (CompressionEncodingAlgorithm has only NONE and gzip). When nothing in the list is supported we throw, where C2J warns and emits no call: silently dropping compression changes wire behavior with no signal. A @streaming @requiresLength payload is rejected (Smithy forbids it; the compressed length isn't known until the stream has been read).
  • @httpChecksumRequired → ChecksumUtils.SetChecksumData(request); after body serialization (the checksum covers the body), before the DisablePayloadSigning line. This is the legacy MD5-only trait. C2J also treats the flexible aws.protocols#httpChecksum as MD5-required; we deliberately don't (that trait is rejected as unsupported), so flexible-checksum operations stay off the MD5 path.
@endpoint host prefix (request)

An operation's @endpoint trait sets request.HostPrefix (the resolver prepends it to the endpoint host). Emitted last, after UseQueryString, before return.

  • Static (no labels) → request.HostPrefix = $"data.";
  • Labeled → each @hostLabel member is captured into an anonymous hostPrefixLabels object (field = modeled member name, value = StringUtils.FromString(publicRequest.{Prop})), validated with HostPrefixUtils.IsValidLabelValue (throws Amazon{Service}Exception naming the label and the 1–63 alphanumeric/dash rule), then interpolated into the prefix ({name} → {hostPrefixLabels.name}).

Response Unmarshaller Body

The body loop is while (context.ReadAtDepth(targetDepth, ref reader)) with a context.TestExpression("{wireName}", targetDepth, ref reader) guard per member assigning unmarshalledObject.{Prop} = {Unmarshaller}.Instance.Unmarshall(context, ref reader) then continue. A response whose members are all headers (or empty) emits no reader loop at all.

  • Lists: new JsonListUnmarshaller<T, TUnmarshaller>(TUnmarshaller.Instance)
  • Maps: new JsonDictionaryUnmarshaller<string, V, StringUnmarshaller, VU>(StringUnmarshaller.Instance, VU.Instance) (key is always string/StringUnmarshaller).
  • Scalars per Type → Marshal/Unmarshal. Body timestamps are read format-agnostically, so epoch and date-time collections unmarshal identically.
  • Nested collections compose recursively: a map-of-list is JsonDictionaryUnmarshaller<string, List<T>, StringUnmarshaller, JsonListUnmarshaller<T, TU>>(...). An enum leaf (and an enum key) is string/StringUnmarshaller, never a ConstantClass generic argument; a non-streaming blob leaf is MemoryStream/MemoryStreamUnmarshaller.
@httpPayload (response)

A @httpPayload output member IS the whole body (replaces the named-field loop; other members are header-bound), into unmarshalledObject:

  • String → using (var sr = new StreamReader(context.Stream)) { unmarshalledObject.Body = sr.ReadToEnd(); }
  • Structure → reader + if (reader.Reader.IsFinalBlock) return unmarshalledObject; + {Type}Unmarshaller.Instance.Unmarshall(context, ref reader).
  • Document → the structure scaffold with Amazon.Runtime.Documents.Internal.Transform.DocumentUnmarshaller.Instance. C2J models a document as a structure flagged document: true, so its output takes the structure-payload branch; we emit the same (bedrock-agentcore GetAgentCardResponse).
  • Blob (non-streaming, MemoryStream) → Amazon.Util.AWSSDKUtils.CopyStream(context.Stream, ms) into a new MemoryStream; assigned only when ms.Length > 0, so an empty body leaves the property null (matches C2J).
  • Streaming blob (@streaming, Stream) → assigns the raw context.Stream unbuffered and the unmarshaller class overrides public override bool HasStreamingProperty => true (matches C2J, see Polly SynthesizeSpeech).

@httpHeader members read from context.ResponseData after. An @httpPayload error member fails loud: C2J never bound an error body to a payload, and emitting a never-populated property would be worse.

@httpResponseCode (response)

An @httpResponseCode output member (an integer) is populated from the HTTP status code itself, unmarshalledObject.{Prop} = (int)context.ResponseData.StatusCode;, not read from the body or a header. Matches C2J. On an error the trait "is simply ignored" (Smithy spec) and the member falls through to the body like any ordinary member.

Show full SKILL.md (1,473 more words)Show less
Event streams (response)

An output member targeting a @streaming union/structure is an event stream. It IS the body: the unmarshaller emits unmarshalledObject.{Prop} = new {UnionClass}(context.Stream); instead of a JSON reader loop (C2J parity), and the class overrides HasStreamingProperty => true and ShouldReadEntireResponse(...) => false so Core never buffers the body. The {UnionClass} itself, and everything else event streams emit, is in sdk-conventions → Event Streams.

Each non-error union member targets an event structure that gets its own {Event}Unmarshaller, invoked per event from the event-stream class's EventMapping. A member carries at most one of @eventPayload/@eventHeader:

  • @eventPayload (≤1 per event) → the raw message payload, no JSON body loop for it. Blob → unmarshalledObject.{Prop} = context.Stream as MemoryStream;; string → using (var sr = new StreamReader(context.Stream)) { unmarshalledObject.{Prop} = sr.ReadToEnd(); }; structure → unmarshalledObject.{Prop} = {Type}Unmarshaller.Instance.Unmarshall(context, ref reader);. The SEP requires all other members to carry @eventHeader when a payload member exists.
  • @eventHeader → read from the event-message header via context.ResponseData, guarded by IsEventHeaderPresent("{ModeledName}") (the wire header key is the member name verbatim). The accessor on GetEventStreamHeader("{ModeledName}") follows the target shape: string/enum → AsString(), boolean → AsBool(), integer/intEnum → AsInt32(), long → AsInt64(), timestamp → AsTimestamp(), blob → new MemoryStream(...AsByteBuf()). Any other target fails loud.
  • unbound → the JSON body, read through the ordinary reader loop.

An event can carry @eventHeader members without an @eventPayload member: a headers-only event (only header reads, no reader loop) or an implicit-payload event (headers from headers, the rest from the JSON body). This is not an all-or-nothing branch (C2J gets this wrong; the Smithy protocol tests HeadersEvent/HeadersAndImplicitPayloadEvent cover it). The reader loop is skipped only for events with an explicit payload or headers alone; an ordinary structure with no members still runs it, so the {} tokens are consumed and the parent's remaining members are not misread.

Event streams (request)

A request event stream adds a publisher marshaller on top of the marker interface and per-event partials described in sdk-conventions. The union gets no plain model class or structure marshaller, but its event members still get marshallers.

  • {Union}PublisherMarshaller: NextEventAsync pulls the consumer's next event, dispatches on evnt is {Event}, marshals it with {Event}Marshaller.Instance, and sets eventType to the union member name verbatim (the wire :event-type, not the shape name). The wire :content-type follows the event's @eventPayload member: a blob payload → application/octet-stream, a string payload → text/plain, both read from context.Request.Content; a structure payload or an implicit body → application/json from the JSON writer's stream. (C2J emits text/plain for a structure payload too, but the event marshaller writes it as JSON, so JSON is kept here.) CBOR is not handled: restJson1 only.
  • The request member becomes a Func<Task<I{Union}Event>> {Member}Publisher property (keeps any modeled [AWSProperty]/[Obsolete], no IsSet). The request marshaller wires it: request.EventStreamPublisher = new {Union}PublisherMarshaller(publicRequest.{Member}Publisher) with Content-Type: application/vnd.amazon.eventstream, replacing body serialization.

Response Header Unmarshalling

Output and error members bound with @httpHeader are read from the HTTP response headers via context.ResponseData, not the body reader; they're extracted after the reader loop, into unmarshalledObject on both the response and the exception path.

Each member is guarded by context.ResponseData.IsHeaderPresent("x-foo") and assigned a <conversion>:

Member target<conversion> (with value = context.ResponseData.GetHeaderValue("x-foo"))
string / enumvalue (direct; enum rides the string path via implicit ConstantClass conversion)
booleanbool.Parse(value) (no culture: its two literals are culture-invariant)
integer / intEnumint.Parse(value, CultureInfo.InvariantCulture)
longlong.Parse(value, CultureInfo.InvariantCulture)
floatfloat.Parse(value, CultureInfo.InvariantCulture)
doubledouble.Parse(value, CultureInfo.InvariantCulture)
timestamp, date-time / http-dateDateTime.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)
timestamp, epoch-secondsAmazon.Util.AWSSDKUtils.ConvertFromUnixEpochSeconds(int.Parse(value, CultureInfo.InvariantCulture))
list<string> / list<enum>MultiValueHeaderParser.ToStringList(value)
list<value-type> (int/long/bool/double/float)MultiValueHeaderParser.ToValueTypeList<T>(value) (T = the non-nullable element type, e.g. int)
list<timestamp>MultiValueHeaderParser.ToDateTimeList(value, "FMT"), where FMT is the runtime's format name (RFC822/ISO8601/UnixTimestamp, header default RFC822), not the Smithy token

A non-scalar list element fails loud, as does a @sparse value-type element. MultiValueHeaderParser lives in Amazon.Runtime.Internal.Util; CultureInfo/DateTimeStyles come from System.Globalization. Both namespaces are imported unconditionally.

@httpPrefixHeaders (response / error)

A map<string, string> member bound with @httpPrefixHeaders collects every response header whose name starts with the prefix into a local dictionary named headersFor{Property} (matching C2J), stripping the prefix from each key. An empty prefix collects all headers. Assigned only when non-empty (matches C2J). Same output on the response and exception unmarshallers.

Type → Marshal/Unmarshal

Smithy target.NETJSON MarshalJSON Unmarshal
string / enumstring / ConstantClassWriteStringValueStringUnmarshaller
integer / intEnumint?WriteNumberValueIntUnmarshaller
longlong?WriteNumberValueLongUnmarshaller
booleanbool?WriteBooleanValueBoolUnmarshaller
floatfloat?WriteNumberValue (NaN/∞ as strings, members only)FloatUnmarshaller
doubledouble?WriteNumberValue (NaN/∞ as strings, members only)DoubleUnmarshaller
timestampDateTime?Format-dependent (see below)DateTimeUnmarshaller
blobMemoryStreamStringUtils.WriteBase64StringValue(context.Writer, ...)MemoryStreamUnmarshaller
documentAmazon.Runtime.Documents.DocumentDocumentMarshaller.Instance.WriteDocumentUnmarshaller.Instance
listList<T>Array loopJsonListUnmarshaller<ElementType, ElementUnmarshaller>
mapDictionary<string,V>Object loopJsonDictionaryUnmarshaller<string, V, StringUnmarshaller, ValueUnmarshaller>
structure / unionGenerated class{Shape}Marshaller.Instance{Shape}Unmarshaller.Instance

A value type that is nullable in its position (a member, or a @sparse element; see type-mapping → Nullability Rules) unwraps with .Value before the write and takes the Nullable*Unmarshaller; a non-sparse element writes bare and takes the plain unmarshaller.

Timestamp Formats

An explicit @timestampFormat (on the member or its target) always wins. When unset, the default is per binding, not one per protocol.

@timestampFormatMarshal (body)Marshal (header/query/label)
date-timeWriteStringValue(StringUtils.FromDateTimeToISO8601WithOptionalMs(value))StringUtils.FromDateTimeToISO8601WithOptionalMs(value)
http-dateWriteStringValue(StringUtils.FromDateTimeToRFC822(value))StringUtils.FromDateTimeToRFC822(value)
epoch-secondsWriteNumberValue(Amazon.Util.AWSSDKUtils.ConvertToUnixEpochSecondsDecimal(value.Value))StringUtils.FromDateTimeToUnixTimestamp(value)

restJson1 defaults when @timestampFormat is unset (matches C2J's output). They apply when marshalling and when reading headers; body reads auto-detect the wire format.

BindingDefault
Body / structure memberepoch-seconds (restJson1's document-timestamp default per the Smithy spec)
@httpHeaderhttp-date
@httpQuery, @httpLabeldate-time

String forms pass the nullable DateTime? straight to the StringUtils overload; the epoch form unwraps with .Value. epoch-seconds in a body is a JSON number that may carry a fraction, so it goes through ConvertToUnixEpochSecondsDecimal (millisecond precision, decimal for identical digits on every TFM), not the whole-second StringUtils.FromDateTimeToUnixTimestamp. Header/query/label positions are whole seconds, matching C2J.

Data Type Swaps

A dataTypeSwap member (see type-mapping → Data Type Swaps) carries MarshallerOverride / UnmarshallerOverride on its TypeDescriptor. Only the shared scalar helpers honor them, so a new protocol writer gets swaps by reusing those helpers; an omitted override keeps the modeled conversion:

  • JSON body (JsonScalarMarshaller.WriteScalar): context.Writer.WriteStringValue({Marshaller}(x)), or WriteNumberValue when the marshaller is Amazon.Util.AWSSDKUtils.ConvertToUnixEpochMilliseconds (C2J keys on the name, assuming every other returns a string). .Value is unwrapped first when the modeled type is a value type and the swapped type is nullable.
  • Query / header (JsonRequestMarshallerWriter.StringConversion, and the header branch that assigns strings directly): {Marshaller}(publicRequest.X), passed the property as-is (no .Value). A required swapped query member is checked with == null, since its type may not be a string.
  • Unmarshal (JsonBodyMemberUnmarshaller.ScalarUnmarshaller): var unmarshaller = {Unmarshaller}.Instance;, emitted verbatim.
  • A response header swap that names an Unmarshaller fails loud (not supported yet).

Error Dispatch

In {Operation}ResponseUnmarshaller.UnmarshallException, each error is matched with errorResponse.Code != null && errorResponse.Code.Equals("{smithyShapeName}") (the Smithy shape name, not the .NET exception name) and dispatched to {Exception}Unmarshaller.Instance.Unmarshall(contextCopy, errorResponse, ref readerCopy). Fallback: new Amazon{Service}Exception(errorResponse.Message, ...).

Exception Unmarshaller

Unmarshall(JsonUnmarshallerContext context, ErrorResponse errorResponse, ref StreamingUtf8JsonReader reader) constructs the exception from the six errorResponse fields (message, inner exception, type, code, request id, status code). When the error has body-bound members beyond message, a single if (context.Stream.Length > 0) wraps the first-token read and the body loop, so an empty error body skips both. @httpHeader members are then read from context.ResponseData (see Response Header Unmarshalling).

awsJson1.0 / awsJson1.1

Same JSON body (un)marshalling as restJson1. Every operation is POST / with X-Amz-Target: {ServiceShapeName}.{OperationName} (the service's shape name, not its sdkId) and Content-Type: application/x-amz-json-1.0|1.1; an operation with no input members sends {}. HTTP binding traits and @jsonName are ignored when a model carries them. Spec: protocol behaviors.

1.0 and 1.1 differ only in the Content-Type version and the __type form of errors (differences); Core handles the __type difference transparently, so the generator only varies the version string.

Every one of those decisions is gated on GenerationContext.UsesHttpBindings; AwsJsonCodegenTests pins the emitted code and the JSONRPC10/JsonProtocol protocol tests verify it end to end.

rpcv2Cbor

Spec: Smithy RPC v2 CBOR. Every operation is POST service/{ServiceShapeName}/operation/{OperationName} with smithy-protocol: rpc-v2-cbor, Accept: application/cbor, and a CBOR map body with Content-Type: application/cbor unless the input is Unit. HTTP binding traits, @jsonName and @timestampFormat are ignored: keys are member names and timestamps are tag 1. The output is wire-compatible with C2J's CBOR templates and formatted like the JSON writers' output; the Cbor* writers mirror the JSON ones and are pinned by RpcV2CborCodegenTests.

A @sparse element writes CBOR null per the spec; C2J writes {} for a null structure element in a sparse list, but no CBOR service model uses @sparse. Documents and streaming blobs are rejected by UnsupportedTraitValidator; event streams are not supported.

awsQueryCompatible

Service trait for awsJson1.0 and rpcv2Cbor services that moved off awsQuery (spec): the request announces query mode and errors still dispatch on the shape name, not the rewritten query error code.

Other Protocols (not yet implemented)

restXml, awsQuery and ec2Query: the target output is defined by the C2J templates (generator/ServiceClientGeneratorLib/Generators/Marshallers/*.tt). awsQuery/ec2Query route via an Action={Operation} param with URL-encoded bodies; restXml keeps the HTTP binding traits with an XML body (@xmlName/@xmlFlattened/@xmlAttribute/@xmlNamespace) and, per the Smithy spec, defaults body timestamps to date-time. The XML-response family uses XmlResponseUnmarshaller. Wire names: restXml @xmlName, awsQuery the member name verbatim, ec2Query @ec2QueryName or the member name with its first letter upper-cased.

When implementing one, replace this note with the real patterns and pin the emitted code in codegen tests.

© aws, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in generator/SmithyDotNet/skills/marshalling of aws/aws-sdk-net.

Open the folder on GitHubat commit f36df89

Compare with similar skills

Marshalling next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Marshalling compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Marshalling this skillaws/aws-sdk-net145—~8.2kAutomated safety check: PassApache-2.0
Generatealirezarezvani/claude-skills28k1 repos~1.1kAutomated safety check: PassMIT
Responsive Unitsthedaviddias/Front-End-Checklist74k—~472Automated safety check: PassMIT
Fal Generatenexu-io/open-design100k—~306Automated safety check: PassApache-2.0
Runbook Generatoralirezarezvani/claude-skills28k—~535Automated safety check: PassMIT
Video Generationbytedance/deer-flow83k3 repos~1.4kAutomated safety check: PassMIT

Similar skills

  • Generate

    alirezarezvani/claude-skills

    Generate Playwright tests. An agent skill from alirezarezvani/claude-skills.

    28k GitHub starsUsed in 1 repo~1.1k tokens
    Testing & QAAuto-check passed
  • Responsive Units

    thedaviddias/Front-End-Checklist

    A skill your agent uses when reviewing stylesheets, component styles, and responsive behavior related to Use relative units for responsive layouts.

    74k GitHub stars~472 tokensUpdated yesterday
    Frontend & DesignAuto-check passed
  • Fal Generate

    nexu-io/open-design

    Generate images and videos using fal.ai AI models. An agent skill from nexu-io/open-design.

    100k GitHub stars~306 tokensUpdated today
    Media & CreativeAuto-check passed
  • Runbook Generator

    alirezarezvani/claude-skills

    Generate operational runbooks from a service name — deployment, incident response, maintenance, and rollback workflows.

    28k GitHub stars~535 tokensUpdated 1 mo ago
    DevOps & CloudAuto-check passed
  • Video Generation

    bytedance/deer-flow

    Generates short videos from a structured JSON prompt, optionally guided by a reference image used as the first or last frame.

    83k GitHub starsUsed in 3 repos~1.4k tokens
    Media & CreativeAuto-check passed
  • Image Generation

    onyx-dot-app/onyx

    Generate or edit raster images (photos, illustrations, textures, sprites, mockups, logos, infographics) using the workspace's configured image-generation provider via onyx-cli image.

    32k GitHub starsUsed in 1 repo~1.7k tokens
    Media & CreativeAuto-check passed

More from aws/aws-sdk-net

  • Official

    A skill your agent uses when validating that a C2J-to-Smithy migrated AWS SDK service builds, packages, and stays API-compatible with the shipping SDK, or before reporting such a migration as done…

    145 GitHub stars~4k tokensUpdated yesterday
    Auto-check passed
  • AWS SDK Net Maintainer

    aws/aws-sdk-net

    Official

    A skill your agent uses when working on the AWS SDK for .NET source code itself, including Core runtime changes, service client implementations, generator or model changes, repo-specific build and…

    145 GitHub stars~503 tokensUpdated yesterday
    Auto-check passed
  • Smithy Ast Model

    aws/aws-sdk-net

    Official

    The Smithy JSON AST facts and model invariants the SmithyDotNet generator relies on.

    145 GitHub stars~1.4k tokensUpdated yesterday
    Auto-check passed
  • Type Mapping

    aws/aws-sdk-net

    Official

    Smithy shape to .NET type mapping, nullability, collection element rules, and error-shape naming/member rules.

    145 GitHub stars~2.1k tokensUpdated yesterday
    Auto-check passed
  • SDK Conventions

    aws/aws-sdk-net

    Official

    The public-API contract SmithyDotNet-generated code must match against the shipping AWS SDK for .NET - what must match vs.

    145 GitHub stars~6.3k tokensUpdated yesterday
    Auto-check passed

Questions about Marshalling

What does Marshalling do?

What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol. Marshalling is an agent skill from aws/aws-sdk-net, published by the product's own GitHub organization. What the SmithyDotNet generator must emit for request marshallers and response/error unmarshallers, per Smithy binding trait and protocol.

When should I use Marshalling?

Marshalling fits situations like: reviewing any marshaller/unmarshaller writer.

How do I install Marshalling in Claude Code?

Run `npx skills add aws/aws-sdk-net --skill marshalling -a claude-code`. Or copy the skill folder (generator/SmithyDotNet/skills/marshalling in aws/aws-sdk-net) into .claude/skills/marshalling in your project. Claude Code loads it when a task matches its description.

How do I install Marshalling in Codex?

Run `npx skills add aws/aws-sdk-net --skill marshalling -a codex`. Or copy the skill folder (generator/SmithyDotNet/skills/marshalling in aws/aws-sdk-net) into .agents/skills/marshalling in your project. Codex loads it when a task matches its description.

Can I use Marshalling in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add aws/aws-sdk-net --skill marshalling -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/marshalling, .gemini/skills/marshalling, .github/skills/marshalling and .opencode/skills/marshalling in your project.

What does Marshalling need to run?

SKILL.md names no scripts, command-line tools or credentials: Marshalling is instructions for the agent only.

Does Marshalling access the network?

SKILL.md names 1 domain. As links in the text: smithy.io. This is read from the text; nothing was executed.

Is Marshalling safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Marshalling use?

Marshalling is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Marshalling use?

About 8.2k tokens (SKILL.md is roughly 33k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Marshalling?

Skills that share tags, products or a category with Marshalling: Generate (alirezarezvani/claude-skills, 28k stars), Responsive Units (thedaviddias/Front-End-Checklist, 74k stars), Fal Generate (nexu-io/open-design, 100k stars) and Runbook Generator (alirezarezvani/claude-skills, 28k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Marshalling?

aws (a GitHub organization, an official publisher) maintains it in aws/aws-sdk-net, which has 145 GitHub stars. The repository holds 6 skills in this directory. The repository was last updated on October 6, 2026.

Source: aws/aws-sdk-net on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.