nupp.serde.json
This codec writes and reads JSON text through any nupp.serde.Binding. See Serialization bindings for field mappings.
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 with defaultPolicy, which reads declaration bindings and documents. A model library passes a PolicyFactory of its own to codec, or 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.
Module contents
Types
| Type | Kind | Description |
|---|---|---|
Codec | interface | A JSON codec with its own policy and its own cache of what it selected for each binding. |
FieldName | record | The wire field a JSON decoder reports for a member the model does not declare. |
Policy | interface | Chooses each member's JSON name, whether it is written, and how its timestamps are represented. |
PolicyFactory | interface | Returns the Policy for a binding's member type. |
Selection | type | Whether a policy writes a member, leaves it out, or writes it redacted. |
Functions
| Function | Kind | Description |
|---|---|---|
codec | function | Makes a Codec. |
decode | function | Decodes JSON text into a value. |
decodeBuffer | function | Decodes a caller-owned Buffer, reading it in place when the provider can. |
decodeDocument | function | Decodes JSON text as document contents, attaching the context it was read under. |
documentPolicy | function | Returns the policy JSON applies inside a document-valued member, or nil when the member is not one of a document's own. |
encode | function | Encodes a value as JSON text. |
encodeDocument | function | Encodes a document's contents as JSON text. |
encodeInto | function | Appends a value's JSON text to a caller-owned Buffer. |
fieldName | function | Returns the JSON member name of a wire field. |
writeValue | function | Writes one value at the current position of a nupp.codec.json.Writer, for embedding bound values in JSON assembled... |
Values
| Value | Kind | Description |
|---|---|---|
defaultPolicy | variable | The policy for declaration bindings and documents, which the module-level functions use. |
identity | variable | Identifies JSON as the codec in a nupp.serde.Context it attaches, so a protocol can tell a document it read from one... |
memberNamePolicy | variable | The policy that writes every member under its own name, traverses every member, and writes timestamps as RFC 3339... |
Types#
Codecinterface#
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. Hold a codec of your own to use another policy, or to bound and clear what it retains.
Members
| Name | Kind | Description |
|---|---|---|
encodeDocument | method | Encodes a document's contents. |
decodeDocument | method | Decodes document contents. |
writeValue | method | Writes one value into a JSON writer. |
encode | method | Encodes a value. |
encodeInto | method | Appends a value to a buffer. |
decode | method | Decodes a value. |
decodeBuffer | method | Decodes a value from a buffer. |
clear | method | Forgets every binding's selections. |
cachedBindings | method | Returns how many bindings have selections retained. |
encodeDocumentmethod#
encodeDocument: function<D>(self, adapter: core.DocumentAdapter<D>, borrows value: D): stringEncodes a document's contents. See encodeDocument.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
adapter | core.DocumentAdapter<D> | |
borrows value | D |
Returns
| Type | Description |
|---|---|
string |
decodeDocumentmethod#
decodeDocument: function<D>(self, adapter: core.DocumentAdapter<D>, bytes: string, context: core.Context?): DDecodes document contents. See decodeDocument.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
adapter | core.DocumentAdapter<D> | |
bytes | string | |
context | core.Context? |
Returns
| Type | Description |
|---|---|
D |
writeValuemethod#
writeValue: function<T>(self, binding: core.Binding<T>, borrows value: T, exclusive sink: json.Writer): nilWrites one value into a JSON writer. See writeValue.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
binding | core.Binding<T> | |
borrows value | T | |
exclusive sink | json.Writer |
Returns
| Type | Description |
|---|---|
nil |
encodemethod#
Encodes a value. See encode.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
binding | core.Binding<T> | |
borrows value | T |
Returns
| Type | Description |
|---|---|
string |
encodeIntomethod#
encodeInto: function<T>(self, binding: core.Binding<T>, borrows value: T, exclusive buffer: text.Buffer): nilAppends a value to a buffer. See encodeInto.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
binding | core.Binding<T> | |
borrows value | T | |
exclusive buffer | text.Buffer |
Returns
| Type | Description |
|---|---|
nil |
decodemethod#
decode: function<T>(self, binding: core.Binding<T>, bytes: string, context: core.Context?): TDecodes a value. See decode.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
binding | core.Binding<T> | |
bytes | string | |
context | core.Context? |
Returns
| Type | Description |
|---|---|
T |
decodeBuffermethod#
decodeBuffer: function<T>(self, binding: core.Binding<T>, exclusive input: text.Buffer, context: core.Context?): TDecodes a value from a buffer. See decodeBuffer.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
binding | core.Binding<T> | |
exclusive input | text.Buffer | |
context | core.Context? |
Returns
| Type | Description |
|---|---|
T |
clearmethod#
clear: function(self): nilForgets every binding's selections.
Arguments
| Name | Type | Description |
|---|---|---|
? | self |
Returns
| Type | Description |
|---|---|
nil |
cachedBindingsmethod#
cachedBindings: function(self): integerReturns how many bindings have selections retained.
Arguments
| Name | Type | Description |
|---|---|---|
? | self |
Returns
| Type | Description |
|---|---|
integer |
FieldNamerecord#
The wire field a JSON decoder reports for a member the model does not declare.
Members
| Name | Kind | Description |
|---|---|---|
name | field | The member name from the JSON object, with its escapes decoded. |
diagnosticLabel | method | Returns the name, for messages such as an unknown member's. |
diagnosticLabelmethod#
diagnosticLabel: function diagnosticLabel(self): stringReturns the name, for messages such as an unknown member's.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
string | the member name |
Policyinterface#
interface Policy<M is core.Member> ...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 | method | Returns the JSON member name of a structure or union member. |
select | method | Returns whether a member is traversed, omitted, or redacted. |
timestamp | method | Encodes a timestamp member's value as JSON. |
parseTimestamp | method | Decodes a timestamp member's JSON value. |
namemethod#
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 |
selectmethod#
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 |
timestampmethod#
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 |
parseTimestampmethod#
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 |
PolicyFactoryinterface#
interface PolicyFactory ...Returns the Policy for a binding's member type.
Members
| Name | Kind | Description |
|---|---|---|
policy | method | Returns the policy for a binding whose value sits at a member. |
policymethod#
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<M> | the policy |
Selectiontype#
type Selection = "traverse" | "omit" | "redact"Whether a policy writes a member, leaves it out, or writes it redacted.
Functions#
codecfunction#
function codec(policy: plans.PolicyFactory?, capacity: integer?): CodecMakes a Codec.
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 |
capacity | integer? | the most bindings to retain selections for; nil is 256 |
Returns
| Type | Description |
|---|---|
Codec | the codec |
decodefunction#
function decode<T>(binding: core.Binding<T>, bytes: string, context: core.Context?): TDecodes 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.
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<T> | 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 |
decodeBufferfunction#
function decodeBuffer<T>(binding: core.Binding<T>, exclusive input: text.Buffer, context: core.Context?): TDecodes a caller-owned Buffer, reading it in place when the provider can.
Type parameters
| Name | Description |
|---|---|
T |
Arguments
| Name | Type | Description |
|---|---|---|
binding | core.Binding<T> | 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 |
decodeDocumentfunction#
function decodeDocument<D>(adapter: core.DocumentAdapter<D>, bytes: string, context: core.Context?): DDecodes JSON text as document contents, attaching the context it was read under.
Type parameters
| Name | Description |
|---|---|
D |
Arguments
| Name | Type | Description |
|---|---|---|
adapter | core.DocumentAdapter<D> | the document adapter, such as |
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 |
documentPolicyfunction#
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 asks this first, so documents nested in its values are read and written the way 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<M>? | the policy, or nil |
encodefunction#
function encode<T>(binding: core.Binding<T>, borrows value: T): stringEncodes a value as JSON text.
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<T> | 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 |
encodeDocumentfunction#
function encodeDocument<D>(adapter: core.DocumentAdapter<D>, borrows value: D): stringEncodes a document's contents as JSON text.
Type parameters
| Name | Description |
|---|---|
D |
Arguments
| Name | Type | Description |
|---|---|---|
adapter | core.DocumentAdapter<D> | the document adapter, such as |
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 |
encodeIntofunction#
function encodeInto<T>(binding: core.Binding<T>, borrows value: T, exclusive buffer: text.Buffer): nilAppends 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<T> | 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 |
fieldNamefunction#
function fieldName(field: core.WireField): stringReturns the JSON member name of a wire field.
Arguments
| Name | Type | Description |
|---|---|---|
field | core.WireField |
Returns
| Type | Description |
|---|---|
string |
writeValuefunction#
function writeValue<T>(binding: core.Binding<T>, borrows value: T, exclusive sink: json.Writer): nilWrites one value at the current position of a nupp.codec.json.Writer, for embedding bound values in JSON assembled by hand.
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<T> | 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#
defaultPolicyvariable#
const defaultPolicy: plans.PolicyFactoryThe 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.
identityvariable#
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.
memberNamePolicyvariable#
const memberNamePolicy: plans.PolicyFactoryThe policy that writes every member under its own name, traverses every member, and writes timestamps as RFC 3339 strings.
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.
local serde = require("nupp.serde")
local adapters = require("nupp.serde.adapters")
local json = require("nupp.serde.json")
local record Member is serde.Member
name: string
index: integer?
end
local title: serde.Binding<string> = new serde.Bound<string, Member>(
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"')