Structured Codec
structured@1.0 turns complex objects into strings with extended ao-types according to the HTTP Structured Field Values (RFC-9651) specification.
This object type is called TABM (Type Annotated Binary Message) in HyperBEAM.
ao-types has the following types:
integer:123float:3.14binary:"abc"|Buffer.from([1,2,3])atom:true|false|null|Symbol("abc")list:[1, 2, 3]empty-binary:""|Buffer.from([])empty-list:[]empty-message:{}
/HyperBEAM/src/dev_mydev.erl
-export([ structured_to/3, structured_from/3 ]).
structured_to(Msg1, Msg2, Opts) ->
Body = maps:get(<<"body">>, Msg1),
OBJ = dev_codec_json:from(Body),
TABM = dev_codec_structured:to(OBJ),
JSON = dev_codec_json:to(TABM),
{ok, JSON}.
structured_from(Msg1, Msg2, Opts) ->
Body = maps:get(<<"body">>, Msg1),
TABM = dev_codec_json:from(Body),
OBJ = dev_codec_structured:from(TABM),
JSON = dev_codec_json:to(OBJ),
{ok, JSON}.Encode JSON with dev_codec_structured:from.
/test/codec-structured.test.js
const cases = [
{ list: [1, true, "abc"] },
{ nested_list: [1, [2, 3]] },
{ a: { b: [1, 2, 3] } },
{ a: [1, 2], b: [3, 4] },
{ empty_list: [], empty_binary: "", empty_message: {} },
]
for (const v of cases) {
const { out } = await hb.post({
path: "/~mydev@1.0/structured_from",
body: JSON.stringify(v),
})
console.log(JSON.parse(out))
}{ list: [1, true, "abc"] }
{
'ao-types': 'list="list"',
list: '"(ao-type-integer) 1", "(ao-type-atom) \\"true\\"", "abc"'
}{ nested_list: [1, [2, 3]] }
{
'ao-types': 'nested_list="list"',
nested_list: '"(ao-type-integer) 1", "(ao-type-list) \\"(ao-type-integer) 2\\", \\"(ao-type-integer) 3\\""'
}{ a: { b: [1, 2, 3] } }
{
a: {
'ao-types': 'b="list"',
b: '"(ao-type-integer) 1", "(ao-type-integer) 2", "(ao-type-integer) 3"'
}
}{ a: [1, 2], b: [3, 4] }
{
a: '"(ao-type-integer) 1", "(ao-type-integer) 2"',
'ao-types': 'a="list", b="list"',
b: '"(ao-type-integer) 3", "(ao-type-integer) 4"'
}{ empty_list: [], empty_binary: "", empty_message: {} }
{
'ao-types': 'empty_binary="empty-binary", empty_list="empty-list", empty_message="empty-message"'
}You can specify ao-types of the values at the same level, annotate keys with (ao-type-[type]), and join multiple entries with , .
Let's decode the encoded values.
/test/codec-structured.test.js
const cases = [
{
'ao-types': 'list="list"',
list: '"(ao-type-integer) 1", "(ao-type-atom) \\"true\\"", "abc"'
},
{
'ao-types': 'nested_list="list"',
nested_list: '"(ao-type-integer) 1", "(ao-type-list) \\"(ao-type-integer) 2\\", \\"(ao-type-integer) 3\\""'
},
{
a: {
'ao-types': 'b="list"',
b: '"(ao-type-integer) 1", "(ao-type-integer) 2", "(ao-type-integer) 3"'
}
},
{
a: '"(ao-type-integer) 1", "(ao-type-integer) 2"',
'ao-types': 'a="list", b="list"',
b: '"(ao-type-integer) 3", "(ao-type-integer) 4"'
},
{
'ao-types': 'empty_binary="empty-binary", empty_list="empty-list", empty_message="empty-message"'
}
]
for (const v of cases) {
const { out } = await hb.post({
path: "/~mydev@1.0/structured_to",
body: JSON.stringify(v),
})
console.log(JSON.parse(out))
}Running Tests
You can find the working test file for this chapter here:
Run tests:
Terminal
yarn test test/codec-structured.test.js