# `nupp.serde.json`
This codec writes and reads JSON text through any `nupp.serde.Binding`. See
[Serialization bindings](../../../../learn/runtime/data/serde/index.html#json-mappings) for field mappings.
```nupp
local serde = require("nupp.serde")
local json = require("nupp.serde.json")
local record Order
id: integer
items: {string}
note: string?
end
local orders = serde.binding(Order)
local bytes = json.encode(orders, new Order(id = 9, items = {"tea"}))
assert(bytes == '{"id":9,"items":["tea"],"note":null}')
local order = json.decode(orders, bytes)
assert(order.items[1] == "tea" and order.note == nil)
```
What a binding needs to be written is worked out on first use and kept, so the
second call with a binding does none of that work again. Native AOT builds also
select a compiled decoder for bindings whose carriers it supports.
The module-level functions share one [`Codec`](#nupp.serde.json.Codec) with [`defaultPolicy`](#nupp.serde.json.defaultPolicy), which
reads declaration bindings and documents. A model library passes a
[`PolicyFactory`](#nupp.serde.json.PolicyFactory) of its own to [`codec`](#nupp.serde.json.codec), or [`memberNamePolicy`](#nupp.serde.json.memberNamePolicy) when its
member names are already the wire names.
Decoding is strict: a declaration binding rejects unknown members unless its
`nupp.serde.Options` say `unknownMembers = "ignore"`, integers must fit their
declared width, and nesting and token counts are bounded. Every failure is a
`nupp.serde.Error` naming the path and byte offset where it happened.
## Types
### `Codec` _interface_
```nupp
interface Codec ...
```
A JSON codec with its own policy and its own cache of what it selected for
each binding.
The module-level functions use one shared codec with [`defaultPolicy`](#nupp.serde.json.defaultPolicy). Hold
a codec of your own to use another policy, or to bound and clear what it
retains.
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`encodeDocument`](#nupp.serde.json.Codec.encodeDocument) | method | Encodes a document's contents. |
| [`decodeDocument`](#nupp.serde.json.Codec.decodeDocument) | method | Decodes document contents. |
| [`writeValue`](#nupp.serde.json.Codec.writeValue) | method | Writes one value into a JSON writer. |
| [`encode`](#nupp.serde.json.Codec.encode) | method | Encodes a value. |
| [`encodeInto`](#nupp.serde.json.Codec.encodeInto) | method | Appends a value to a buffer. |
| [`decode`](#nupp.serde.json.Codec.decode) | method | Decodes a value. |
| [`decodeBuffer`](#nupp.serde.json.Codec.decodeBuffer) | method | Decodes a value from a buffer. |
| [`clear`](#nupp.serde.json.Codec.clear) | method | Forgets every binding's selections. |
| [`cachedBindings`](#nupp.serde.json.Codec.cachedBindings) | method | Returns how many bindings have selections retained. |
#### `encodeDocument` _method_
```nupp
encodeDocument: function(self, adapter: core.DocumentAdapter, borrows value: D): string
```
Encodes a document's contents. See [`encodeDocument`](#nupp.serde.json.encodeDocument).
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `adapter` | `core.DocumentAdapter\` | |
| `borrows value` | `D` | |
##### Returns
| Type | Description |
| --- | --- |
| `string` | |
#### `decodeDocument` _method_
```nupp
decodeDocument: function(self, adapter: core.DocumentAdapter, bytes: string, context: core.Context?): D
```
Decodes document contents. See [`decodeDocument`](#nupp.serde.json.decodeDocument).
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `adapter` | `core.DocumentAdapter\` | |
| `bytes` | `string` | |
| `context` | `core.Context?` | |
##### Returns
| Type | Description |
| --- | --- |
| `D` | |
#### `writeValue` _method_
```nupp
writeValue: function(self, binding: core.Binding, borrows value: T, exclusive sink: json.Writer): nil
```
Writes one value into a JSON writer. See [`writeValue`](#nupp.serde.json.writeValue).
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `binding` | `core.Binding\` | |
| `borrows value` | `T` | |
| `exclusive sink` | `json.Writer` | |
##### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### `encode` _method_
```nupp
encode: function(self, binding: core.Binding, borrows value: T): string
```
Encodes a value. See [`encode`](#nupp.serde.json.encode).
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `binding` | `core.Binding\` | |
| `borrows value` | `T` | |
##### Returns
| Type | Description |
| --- | --- |
| `string` | |
#### `encodeInto` _method_
```nupp
encodeInto: function(self, binding: core.Binding, borrows value: T, exclusive buffer: text.Buffer): nil
```
Appends a value to a buffer. See [`encodeInto`](#nupp.serde.json.encodeInto).
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `binding` | `core.Binding\` | |
| `borrows value` | `T` | |
| `exclusive buffer` | `text.Buffer` | |
##### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### `decode` _method_
```nupp
decode: function(self, binding: core.Binding, bytes: string, context: core.Context?): T
```
Decodes a value. See [`decode`](#nupp.serde.json.decode).
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `binding` | `core.Binding\` | |
| `bytes` | `string` | |
| `context` | `core.Context?` | |
##### Returns
| Type | Description |
| --- | --- |
| `T` | |
#### `decodeBuffer` _method_
```nupp
decodeBuffer: function(self, binding: core.Binding, exclusive input: text.Buffer, context: core.Context?): T
```
Decodes a value from a buffer. See [`decodeBuffer`](#nupp.serde.json.decodeBuffer).
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `binding` | `core.Binding\` | |
| `exclusive input` | `text.Buffer` | |
| `context` | `core.Context?` | |
##### Returns
| Type | Description |
| --- | --- |
| `T` | |
#### `clear` _method_
```nupp
clear: function(self): nil
```
Forgets every binding's selections.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
##### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### `cachedBindings` _method_
```nupp
cachedBindings: function(self): integer
```
Returns how many bindings have selections retained.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
##### Returns
| Type | Description |
| --- | --- |
| `integer` | |
### `FieldName` _record_
```nupp
record FieldName is core.WireField ...
```
The wire field a JSON decoder reports for a member the model does not
declare.
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`name`](#nupp.serde.json.FieldName.name) | field | The member name from the JSON object, with its escapes decoded. |
| [`diagnosticLabel`](#nupp.serde.json.FieldName.diagnosticLabel) | method | Returns the name, for messages such as an unknown member's. |
#### `name` _field_
```nupp
name: string
```
`@readonly`
The member name from the JSON object, with its escapes decoded.
#### `diagnosticLabel` _method_
```nupp
diagnosticLabel: function diagnosticLabel(self): string
```
Returns the name, for messages such as an unknown member's.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | |
##### Returns
| Type | Description |
| --- | --- |
| `string` | the member name |
### `Policy` _interface_
```nupp
interface Policy ...
```
Chooses each member's JSON name, whether it is written, and how its
timestamps are represented.
A model library implements one to interpret the traits on its own members,
such as a Smithy `jsonName` or `timestampFormat`.
#### Type parameters
| Name | Description |
| --- | --- |
| `M` | |
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`name`](#nupp.serde.json.Policy.name) | method | Returns the JSON member name of a structure or union member. |
| [`select`](#nupp.serde.json.Policy.select) | method | Returns whether a member is traversed, omitted, or redacted. |
| [`timestamp`](#nupp.serde.json.Policy.timestamp) | method | Encodes a timestamp member's value as JSON. |
| [`parseTimestamp`](#nupp.serde.json.Policy.parseTimestamp) | method | Decodes a timestamp member's JSON value. |
#### `name` _method_
```nupp
name: function(self, member: M): string
```
Returns the JSON member name of a structure or union member.
The codec asks while planning a binding, and as it writes each member
of a dynamic value such as a document. Two members of a structure that
this policy gives the same name fail the plan unless one of them is omitted.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `member` | `M` | the member to name |
##### Returns
| Type | Description |
| --- | --- |
| `string` | the JSON name |
#### `select` _method_
```nupp
select: function(self, member: M, direction: core.Direction): Selection
```
Returns whether a member is traversed, omitted, or redacted.
The codec asks about every member a binding reaches when it plans it
for a direction. Reading fails the plan when a member that is not
traversed is required and has no default.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `member` | `M` | the member to select |
| `direction` | `core.Direction` | whether the plan is for writing or reading |
##### Returns
| Type | Description |
| --- | --- |
| `Selection` | the selection |
#### `timestamp` _method_
```nupp
timestamp: function(self, member: M, borrows value: core.Timestamp): json.EncodedValue
```
Encodes a timestamp member's value as JSON.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `member` | `M` | the member being written |
| `borrows value` | `core.Timestamp` | the timestamp |
##### Returns
| Type | Description |
| --- | --- |
| `json.EncodedValue` | the encoded JSON value, such as an RFC 3339 string or a number of epoch seconds |
#### `parseTimestamp` _method_
```nupp
parseTimestamp: function(self, member: M, text: string, kind: "string" | "number"): core.Timestamp
```
Decodes a timestamp member's JSON value.
The codec rejects a value that is neither a string nor a number before
asking.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `member` | `M` | the member being read |
| `text` | `string` | the string's contents, or the number's lexeme |
| `kind` | `"string" | "number"` | which of the two the input held |
##### Returns
| Type | Description |
| --- | --- |
| `core.Timestamp` | the timestamp |
### `PolicyFactory` _interface_
```nupp
interface PolicyFactory ...
```
Returns the [`Policy`](#nupp.serde.json.Policy) for a binding's member type.
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`policy`](#nupp.serde.json.PolicyFactory.policy) | method | Returns the policy for a binding whose value sits at a member. |
#### `policy` _method_
```nupp
policy: function(self, member: M): Policy
```
Returns the policy for a binding whose value sits at a member.
A codec asks when it first plans a binding for a direction, and keeps
the policy with that plan while the binding stays in its cache. The
one policy serves every member the binding reaches.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `?` | `self` | |
| `member` | `M` | the member the binding's root value sits at |
##### Returns
| Type | Description |
| --- | --- |
| `Policy\` | the policy |
### `Selection` _type_
```nupp
type Selection = "traverse" | "omit" | "redact"
```
Whether a policy writes a member, leaves it out, or writes it redacted.
## Functions
### `codec` _function_
```nupp
function codec(policy: plans.PolicyFactory?, capacity: integer?): Codec
```
Makes a [`Codec`](#nupp.serde.json.Codec).
```nupp
local serde = require("nupp.serde")
local json = require("nupp.serde.json")
local record Item
id: integer
end
local codec = json.codec(json.defaultPolicy, 16)
local items = serde.binding(Item)
assert(codec:decode(items, codec:encode(items, new Item(id = 3))).id == 3)
assert(codec:cachedBindings() == 1)
codec:clear()
```
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `policy` | `plans.PolicyFactory?` | the policy; nil is [`defaultPolicy`](#nupp.serde.json.defaultPolicy) |
| `capacity` | `integer?` | the most bindings to retain selections for; nil is 256 |
#### Returns
| Type | Description |
| --- | --- |
| `Codec` | the codec |
### `decode` _function_
```nupp
function decode(binding: core.Binding, bytes: string, context: core.Context?): T
```
Decodes JSON text into a value.
The whole input must be one value; trailing text is an error. A decoded
value the codec then has to reject is dropped through the binding.
```nupp
local serde = require("nupp.serde")
local json = require("nupp.serde.json")
local record Range
low: integer
high: integer = 100
end
local range = json.decode(serde.binding(Range), '{"low":5}')
assert(range.low == 5 and range.high == 100)
```
#### Type parameters
| Name | Description |
| --- | --- |
| `T` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `binding` | `core.Binding\` | the binding to construct through |
| `bytes` | `string` | the JSON text |
| `context` | `core.Context?` | a parent for the context the decoder attaches, or nil |
#### Returns
| Type | Description |
| --- | --- |
| `T` | the value |
#### Raises
| Type | Condition |
| --- | --- |
| `nupp.serde.Error` | with the path and byte offset of the failure |
### `decodeBuffer` _function_
```nupp
function decodeBuffer(binding: core.Binding, exclusive input: text.Buffer, context: core.Context?): T
```
Decodes a caller-owned Buffer, reading it in place when the provider can.
#### Type parameters
| Name | Description |
| --- | --- |
| `T` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `binding` | `core.Binding\` | the binding to construct through |
| `exclusive input` | `text.Buffer` | the buffer holding the JSON text |
| `context` | `core.Context?` | a parent for the context the decoder attaches, or nil |
#### Returns
| Type | Description |
| --- | --- |
| `T` | the value |
#### Raises
| Type | Condition |
| --- | --- |
| `nupp.serde.Error` | with the path and byte offset of the failure |
### `decodeDocument` _function_
```nupp
function decodeDocument(adapter: core.DocumentAdapter, bytes: string, context: core.Context?): D
```
Decodes JSON text as document contents, attaching the context it was read
under.
#### Type parameters
| Name | Description |
| --- | --- |
| `D` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `adapter` | `core.DocumentAdapter\` | the document adapter, such as `nupp.serde.document.adapter` |
| `bytes` | `string` | the JSON text |
| `context` | `core.Context?` | a parent for the attached context, or nil |
#### Returns
| Type | Description |
| --- | --- |
| `D` | the document |
#### Raises
| Type | Condition |
| --- | --- |
| `nupp.serde.Error` | with the path and byte offset of the failure |
### `documentPolicy` _function_
```nupp
function documentPolicy(member: M, epochTimestamps: boolean): plans.Policy?
```
Returns the policy JSON applies inside a document-valued member, or nil when
the member is not one of a document's own.
A model's [`PolicyFactory`](#nupp.serde.json.PolicyFactory) asks this first, so documents nested in its
values are read and written the way [`defaultPolicy`](#nupp.serde.json.defaultPolicy) reads them.
#### Type parameters
| Name | Description |
| --- | --- |
| `M` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `member` | `M` | the member a codec asks about |
| `epochTimestamps` | `boolean` | whether document timestamps are written as epoch seconds |
#### Returns
| Type | Description |
| --- | --- |
| `plans.Policy\?` | the policy, or nil |
### `encode` _function_
```nupp
function encode(binding: core.Binding, borrows value: T): string
```
Encodes a value as JSON text.
```nupp
local serde = require("nupp.serde")
local json = require("nupp.serde.json")
local record Pair
left: string
right: string?
end
assert(json.encode(serde.binding(Pair), new Pair(left = "a")) == '{"left":"a","right":null}')
```
#### Type parameters
| Name | Description |
| --- | --- |
| `T` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `binding` | `core.Binding\` | the value's binding |
| `borrows value` | `T` | the value |
#### Returns
| Type | Description |
| --- | --- |
| `string` | the JSON text |
#### Raises
| Type | Condition |
| --- | --- |
| `nupp.serde.Error` | when a value cannot be represented, at its path |
### `encodeDocument` _function_
```nupp
function encodeDocument(adapter: core.DocumentAdapter, borrows value: D): string
```
Encodes a document's contents as JSON text.
#### Type parameters
| Name | Description |
| --- | --- |
| `D` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `adapter` | `core.DocumentAdapter\` | the document adapter, such as `nupp.serde.document.adapter` |
| `borrows value` | `D` | the document |
#### Returns
| Type | Description |
| --- | --- |
| `string` | the JSON text |
#### Raises
| Type | Condition |
| --- | --- |
| `nupp.serde.Error` | when a value has no JSON representation |
### `encodeInto` _function_
```nupp
function encodeInto(binding: core.Binding, borrows value: T, exclusive buffer: text.Buffer): nil
```
Appends a value's JSON text to a caller-owned Buffer.
On failure the buffer is restored to what it held before the call.
#### Type parameters
| Name | Description |
| --- | --- |
| `T` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `binding` | `core.Binding\` | the value's binding |
| `borrows value` | `T` | the value |
| `exclusive buffer` | `text.Buffer` | the buffer to append to |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
| Type | Condition |
| --- | --- |
| `nupp.serde.Error` | when a value cannot be represented, at its path |
### `fieldName` _function_
```nupp
function fieldName(field: core.WireField): string
```
Returns the JSON member name of a wire field.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `field` | `core.WireField` | |
#### Returns
| Type | Description |
| --- | --- |
| `string` | |
### `writeValue` _function_
```nupp
function writeValue(binding: core.Binding, borrows value: T, exclusive sink: json.Writer): nil
```
Writes one value at the current position of a `nupp.codec.json.Writer`,
for embedding bound values in JSON assembled by hand.
```nupp
local serde = require("nupp.serde")
local json = require("nupp.serde.json")
local record Tag
name: string
end
local buffer = nupp.text.newBuffer()
local writer = nupp.codec.json.newWriter(buffer)
writer:startArray()
json.writeValue(serde.binding(Tag), new Tag(name = "x"), writer)
writer:endArray()
writer:close()
assert(buffer:tostring() == '[{"name":"x"}]')
```
#### Type parameters
| Name | Description |
| --- | --- |
| `T` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `binding` | `core.Binding\` | the value's binding |
| `borrows value` | `T` | the value |
| `exclusive sink` | `json.Writer` | the writer, positioned where a value may go |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
| Type | Condition |
| --- | --- |
| `nupp.serde.Error` | when a value cannot be represented, at its path |
## Values
### `defaultPolicy` _variable_
```nupp
const defaultPolicy: plans.PolicyFactory
```
The policy for declaration bindings and documents, which the module-level
functions use. A model's own factory can delegate to it for members it does
not own.
### `identity` _variable_
```nupp
const identity: table
```
Identifies JSON as the codec in a `nupp.serde.Context` it attaches, so a
protocol can tell a document it read from one another codec read.
### `memberNamePolicy` _variable_
```nupp
const memberNamePolicy: plans.PolicyFactory
```
The policy that writes every member under its own `name`, traverses every
member, and writes timestamps as RFC 3339 strings.
[`defaultPolicy`](#nupp.serde.json.defaultPolicy) refuses a model's own members rather than guess at the
traits they carry, so a model library has to say how its members are named.
Use it for a model whose names are already the wire names.
```nupp
local serde = require("nupp.serde")
local adapters = require("nupp.serde.adapters")
local json = require("nupp.serde.json")
local record Member is serde.Member
@readonly name: string
@readonly index: integer?
end
local title: serde.Binding = new serde.Bound(
adapter = adapters.text(new Member(name = "title"))
)
assert(not pcall(json.encode, title, "Dune"))
local plain = json.codec(json.memberNamePolicy)
assert(plain:encode(title, "Dune") == '"Dune"')
```