json-xdr v0.1.1
JSON <-> XDR
This library extends js-xdr to let you convert XDR to JSON and JSON to XDR.
Installation
via npm:
npm install --save json-xdrUsage
The examples below will use the following XDR definition:
const XDR = require('js-xdr')
const types = XDR.config((xdr) => {
xdr.typedef("Hash", xdr.opaque(2));
xdr.typedef('Int32', xdr.int());
xdr.struct("Price", [
["n", xdr.lookup("Int32")],
["d", xdr.lookup("Int32")],
]);
xdr.enum("MemoType", {
memoNone: 0,
memoText: 1,
memoId: 2,
memoHash: 3,
memoReturn: 4,
});
xdr.union("Memo", {
switchOn: xdr.lookup("MemoType"),
switchName: "type",
switches: [
["memoNone", xdr.void()],
["memoText", "text"],
["memoId", "id"]
],
arms: {
text: xdr.string(28),
id: xdr.lookup("Int32")
},
});
xdr.typedef('CounterInt', xdr.option(xdr.int()));
xdr.struct('Event', [
["attendees", xdr.int()],
["eventName", xdr.string(50)],
["secretSpeakers", xdr.array(xdr.lookup("Hash"), 2)],
["speakers", xdr.varArray(xdr.string())],
["price", xdr.lookup("Price")],
["memo", xdr.lookup("Memo")],
['meta', xdr.lookup('TransactionMeta')],
['counter', xdr.lookup("CounterInt")]
])
xdr.enum("TransactionMetaType", {
none: 0,
paid: 1
});
xdr.union("TransactionMeta", {
switchOn: xdr.lookup("TransactionMetaType"),
switches: [
["none", xdr.void()],
["paid", "price"]
],
arms: {
price: xdr.lookup("Price")
},
defaultArm: xdr.void()
});
})Serializing from XDR to JSON
You can convert an XDR struct to JSON using the function toJSON.
import { toJSON } from 'json-xdr';
let event = new types.Event({
attendees: 5,
eventName: "Lumenauts get together",
secretSpeakers: [Buffer.from([0, 0]), Buffer.from([0, 1])],
speakers: ['Jed', 'Tom', 'Zac'],
price: new types.Price({
n: 2,
d: 1
}),
memo: types.Memo.memoText("foo"),
meta: types.TransactionMeta.paid(new types.Price({
n: 2,
d: 1
})),
counter: 2
})
let payload = toJSON(event);
console.log(payload)
// Output
// {
// "attendees": 5,
// "eventName": "Lumenauts get together",
// "secretSpeakers": [
// "AAA=",
// "AAE="
// ],
// "speakers": [
// "Jed",
// "Tom",
// "Zac"
// ],
// "price": {
// "n": 2,
// "d": 1
// },
// "memo": {
// "_type": "memoText",
// "text": "foo"
// },
// "meta": {
// "_type": "paid",
// "price": {
// "n": 2,
// "d": 1
// }
// },
// "counter": 2
// }Serializing from JSON to XDR
Given a JSON object representing a struct from your types definition, you can convert it to XDR using the function toXDR.
import { toXDR } from 'json-xdr';
let payload = {
"attendees": 5,
"eventName": "Lumenauts get together",
"secretSpeakers": [
"AAA=",
"AAE="
],
"speakers": [
"Jed",
"Tom",
"Zac"
],
"price": {
"n": 2,
"d": 1
},
"memo": {
"_type": "memoText",
"text": "foo"
},
"meta": {
"_type": "paid",
"price": {
"n": 2,
"d": 1
}
},
"counter": 2
}
let event = toXDR(types.Event, payload)
assert.ok(xdrEvent instanceof types.Event)Serialization format
JavaScript native types are used when possible, for example, String,
Integer, Array and null. However there are some types which
don't have an equivalent representation. For those types the
serialization and deserialization is done using the following rules.
Opaque and VarOpaque
Opaque and Variable Opaque data are represented in js-xdr using a binary data buffer. Buffers are serializer as a base64 encoded string.
For the following definition:
const types = XDR.config((xdr) => {
xdr.struct('withOpaque', [
['opaque', xdr.opaque(3)],
['varOpaque', xdr.varOpaque(2)]
])
})
let withOpaque = new types.withOpaque({
opaque: Buffer.from([0, 0, 1]),
varOpaque: Buffer.from([0, 1])
})Calling #toJSON will result in:
{
opaque: 'AAAB',
varOpaque: 'AAE='
}Array
Array and VarArray are serialized as a JavaScript Arrays and then for each element we
apply the serialization rules defined in this library.
For the following definition:
const types = XDR.config((xdr) => {
xdr.typedef("Hash", xdr.opaque(2));
xdr.struct('Event', [
["attendees", xdr.int()],
["eventName", xdr.string(50)],
["secretSpeakers", xdr.array(xdr.lookup("Hash"), 2)],
["speakers", xdr.varArray(xdr.string())]
])
})
let event = new types.Event({
attendees: 5,
eventName: "Lumenauts get together",
secretSpeakers: [Buffer.from([0, 0]), Buffer.from([0, 1])],
speakers: ['Jed', 'Tom', 'Zac']
})Calling #toJSON will result in:
{
attendees: 5,
eventName: 'Lumenauts get together',
secretSpeakers: [ 'AAA=', 'AAE=' ],
speakers: [ 'Jed', 'Tom', 'Zac' ]
}Notice how speakers get serialized as a JavaScript String while secretSpeakers which is an Opaque, get serialized as base64, which is documented above.
Union
Unions are serialized as a JavaScript object, the discriminant is
store in the property _type. If there is an arm for the given
discriminant, then a property with the arm's name is set in the object
and its value should contain a type as defined in the XDR declaration.
The following struct has two property of type union: memo and meta.
let event = new types.Event({
attendees: 5,
eventName: "Lumenauts get together",
secretSpeakers: [Buffer.from([0, 0]), Buffer.from([0, 1])],
speakers: ['Jed', 'Tom', 'Zac'],
price: new types.Price({
n: 2,
d: 1
}),
memo: types.Memo.memoText("foo"),
meta: types.TransactionMeta.paid(new types.Price({
n: 2,
d: 1
})),
counter: 2
})The code below shows the result after calling toJSON(event), notice how the memo property has an object
with the key _type: "memoText" and a property matching the arm declaration text with a string value of foo.
Similarly, the property meta has an object with _type: "paid", with the arm price which contains a Struct of type Price.
{
...,
"memo": {
"_type": "memoText",
"text": "foo"
},
"meta": {
"_type": "paid",
"price": {
"n": 2,
"d": 1
}
}
}Default Arms
Given the following definition:
xdr.union("TransactionMeta", {
switchOn: xdr.lookup("TransactionMetaType"),
switches: [
["none", xdr.void()],
["paid", "price"]
],
arms: {
price: xdr.lookup("Price")
},
defaultArm: xdr.string(3)
});If the union instance uses the default arm, it will be serialized like the following:
{
...,
"meta": {
"_type": "pending",
"default": "foo"
}
}Void Arms
If there is no arm for the discriminant, only the _type property will appear on the object.
{
...,
"meta": {
"_type": "withVoidArm",
}
}Examples
You can find more examples in the test here.