Type Mapping
How C# types lower into OpenAPI 3.1 schemas. Property names camelCase by default ([JsonPropertyName] overrides); non-nullable, non-[RivetOptional] members are required.
Primitives
| C# | JSON Schema |
|---|---|
string | string |
bool | boolean |
int / uint / long / ulong / short / ushort / byte / sbyte | integer with format: int32/uint32/int64/uint64/int16/uint16/uint8/int8 (plus min/max bounds for the common widths) |
float / double / decimal | number with format: float/double/decimal |
Guid | string, format: uuid |
DateTime / DateTimeOffset | string, format: date-time (DateTimeOffset additionally carries x-rivet-csharp-type so the import round-trip recovers the exact type) |
DateOnly | string, format: date |
TimeOnly | string, format: time |
Uri | string, format: uri |
byte[] | string, contentEncoding: base64 (the OpenAPI 3.1 idiom — matches the System.Text.Json wire format), plus x-rivet-csharp-type |
char | string with minLength: 1 and maxLength: 1 (System.Text.Json writes a single-character JSON string), plus x-rivet-csharp-type so the import round-trip recovers char |
object | untyped (empty) schema — "any JSON value", emitted deliberately and with no diagnostic. openapi-typescript consumers see unknown; cast at the boundary. |
Composites
- Records / classes →
objectschemas incomponents/schemaswithproperties+required. - Enums →
stringschemas with camelCasedenumvalues ({ Draft, Open }→["draft", "open"]— pair with a camelCaseJsonStringEnumConverterat runtime). - Nullable members (
string?,int?) → 3.1 type arrays ("type": ["string", "null"]); nullable$refs use a null branch. - Collections (
List<T>,IReadOnlyList<T>, arrays) →arraywithitems. - Dictionaries →
objectwithadditionalProperties. Non-string keys add apropertyNamesschema: enum keys$refthe enum schema (which is emitted —Dictionary<Color, int>registersColor), string-backed brand keys$refthe brand schema, and string-serializable primitive keys (Guid,DateTime/DateOnly/TimeOnly,Uri,char, numerics) emittype: stringwith the originalformat(charkeys carry the length-1 bounds instead) plusx-rivet-csharp-typewhere the format alone is ambiguous. Unsupported key types degrade to unconstrained string keys with diagnosticRIV1013. - Generics are monomorphised:
PagedResult<MemberDto>becomes aPagedResult_MemberDtocomponent carryingx-rivet-generic. - Value-object brands: a record with exactly one property named
Value(e.g.record Email(string Value)) lowers to its inner primitive withx-rivet-brand—{ "type": "string", "x-rivet-brand": "Email" }. On the wire it is just the primitive. - Polymorphic hierarchies (
[JsonPolymorphic]/[JsonDerivedType]on a base type) →oneOf+discriminatorwith a complete tag →$refmapping, named after the base. Each registration becomes a{Base}_{Tag}variant component: the discriminator property first (default$type, single-valueenum, required), then the derived type's full flattened property surface — matching System.Text.Json's wire output when serializing as the base type. A derived type referenced directly keeps its own untagged schema (STJ writes no discriminator for it). Non-string tags (RIV1014) and registration-less[JsonPolymorphic](RIV1015) fall back to plain flattening, loudly;UnknownDerivedTypeHandlinghas no spec representation (RIV1016).
Constraint and metadata flow
DataAnnotations and Rivet attributes enrich property schemas: minLength, maxLength, pattern, minimum/maximum, exclusiveMinimum/exclusiveMaximum, multipleOf, minItems/maxItems/uniqueItems, description, default, examples, deprecated, readOnly/writeOnly, format. See Attributes for which attribute produces which keyword — and note these are spec-only; Rivet does not enforce them at runtime (Runtime Validation).
