Skip to content

Commit

Permalink
feat: add decodeFirst to json decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed Sep 11, 2023
1 parent 1b35871 commit a1bd349
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ There are a number of forms where an object will not round-trip precisely, if th

**cborg** can also encode and decode JSON using the same pipeline and many of the same settings. For most (but not all) cases it will be faster to use `JSON.parse()` and `JSON.stringify()`, however **cborg** provides much more control over the process to handle determinism and be more restrictive in allowable forms. It also operates natively with Uint8Arrays rather than strings which may also offer some minor efficiency or usability gains in some circumstances.

Use `import { encode, decode } from 'cborg/json'` to access the JSON handling encoder and decoder.
Use `import { encode, decode, decodeFirst } from 'cborg/json'` to access the JSON handling encoder and decoder.

Many of the same encode and decode options available for CBOR can be used to manage JSON handling. These include strictness requirements for decode and custom tag encoders for encode. Tag encoders can't create new tags as there are no tags in JSON, but they can replace JavaScript object forms with custom JSON forms (e.g. convert a `Uint8Array` to a valid JSON form rather than having the encoder throw an error). The inverse is also possible, turning specific JSON forms into JavaScript forms, by using a custom tokenizer on decode.

Expand Down
14 changes: 12 additions & 2 deletions lib/json/decode.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { decode as _decode } from '../decode.js'
import { decode as _decode, decodeFirst as _decodeFirst } from '../decode.js'
import { Token, Type } from '../token.js'
import { decodeCodePointsArray } from '../byte-utils.js'
import { decodeErrPrefix } from '../common.js'
Expand Down Expand Up @@ -447,4 +447,14 @@ function decode (data, options) {
return _decode(data, options)
}

export { decode, Tokenizer }
/**
* @param {Uint8Array} data
* @param {DecodeOptions} [options]
* @returns {[any, Uint8Array]}
*/
function decodeFirst (data, options) {
options = Object.assign({ tokenizer: new Tokenizer(data, options) }, options)
return _decodeFirst(data, options)
}

export { decode, decodeFirst, Tokenizer }
4 changes: 2 additions & 2 deletions lib/json/json.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { encode } from './encode.js'
import { decode, Tokenizer } from './decode.js'
import { decode, decodeFirst, Tokenizer } from './decode.js'

export { encode, decode, Tokenizer }
export { encode, decode, decodeFirst, Tokenizer }
30 changes: 29 additions & 1 deletion test/test-json.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-env mocha,es2020 */

import { assert } from 'chai'
import { decode, encode } from 'cborg/json'
import { decode, decodeFirst, encode } from 'cborg/json'

const toBytes = (str) => new TextEncoder().encode(str)

Expand Down Expand Up @@ -188,4 +188,32 @@ describe('json basics', () => {
assert.deepStrictEqual(decode(toBytes('{"foo":1,"foo":2}')), { foo: 2 })
assert.throws(() => decode(toBytes('{"foo":1,"foo":2}'), { rejectDuplicateMapKeys: true }), /CBOR decode error: found repeat map key "foo"/)
})

it('decodeFirst', () => {
/*
const encoded = new TextDecoder().decode(encode(obj, sorting === false ? { mapSorter: null } : undefined))
const json = JSON.stringify(obj)
assert.strictEqual(encoded, json)
const decoded = decode(toBytes(JSON.stringify(obj)))
assert.deepStrictEqual(decoded, obj)
*/
let buf = new TextEncoder().encode('{"foo":1,"bar":2}1"ping"2null3[1,2,3]""[[],[],{"boop":true}]')
const expected = [
{ foo: 1, bar: 2 },
1,
'ping',
2,
null,
3,
[1, 2, 3],
'',
[[], [], { boop: true }]
]
for (const exp of expected) {
const [obj, rest] = decodeFirst(buf)
assert.deepStrictEqual(exp, obj)
buf = rest
}
assert.strictEqual(buf.length, 0)
})
})

0 comments on commit a1bd349

Please sign in to comment.