Security News
JSR Working Group Kicks Off with Ambitious Roadmap and Plans for Open Governance
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
bass-clarinet
Advanced tools
SAX based evented streaming JSON parser in Typescript (browser and node)
bass-clarinet
is a port from clarinet
to TypeScript
In addition to the port to TypeScript, the following changes have been made:
onopenobject
no longer includes the first keyJSONTestSuite
is added to the test set. All tests pass.trim
and normalize
options have been dropped. This can be handled by the consumer in the onsimplevalue
callbackcreateStackedDataSubscriber
which pairs onopenobject
/oncloseobject
and onopenarray
/onclosearray
events in a callbackbass-clarinet
is a pure JSON-parser):
allow:angle_brackets_instead_of_brackets
allow:apostrophes_instead_of_quotation_marks
allow:comments
allow:compact
allow:missing_commas
allow:parens_instead_of_braces
allow:schema
allow:trailing_commas
allow:tagged_unions
require:schema
spaces_per_tab
most credits go to the original author Nuno Job
clarinet/bass-clarinet
is a sax-like streaming parser for JSON. works in the browser and node.js. clarinet
was inspired (and forked) from sax-js. just like you shouldn't use sax
when you need dom
you shouldn't use bass-clarinet
when you need JSON.parse
.
Clear reasons to use bass-clarinet
over the built-in JSON.parse
:
bass-clarinet
is very much like yajl but written in TypeScript:
npm install bass-clarinet
.ts
file: import * as bc from "bass-clarinet"
//a simple pretty printer
import * as bc from "bass-clarinet"
import * as fs from "fs"
const [, , path] = process.argv
if (path === undefined) {
console.error("missing path")
process.exit(1)
}
const data = fs.readFileSync(path, {encoding: "utf-8"})
export function createValuesPrettyPrinter(indentation: string, writer: (str: string) => void): bc.ValueHandler {
return {
array: (_startLocation, openCharacter, _comments) => {
writer(openCharacter)
return {
element: () => createValuesPrettyPrinter(`${indentation}\t`, writer),
end: ((_endLocation, endCharacter) => {
writer(`${indentation}${endCharacter}`)
}),
}
},
object: (_startlocation, openCharacter, _comments) => {
writer(openCharacter)
return {
property: (key, _keyRange) => {
writer(`${indentation}\t"${key}": `)
return createValuesPrettyPrinter(`${indentation}\t`, writer)
},
end: (_endLocation, endCharacter) => {
writer(`${indentation}${endCharacter}`)
},
}
},
boolean: (isTrue, _range, _comments) => {
writer(`${isTrue ? "true":"false"}`)
},
number: (value, _range, _comments) => {
writer(`${value.toString(10)}`)//JSON.stringify(value)
},
string: (value, _range, _comments) => {
writer(`${JSON.stringify(value)}`)//JSON.stringify(value)
},
null: _comments => {
writer(`null`)
},
taggedUnion: (option, _unionStart, _optionRange, _comments) => {
writer(`| "${option}" `)
return createValuesPrettyPrinter(`${indentation}`, writer)
},
}
}
export function createPrettyPrinter(indentation: string, writer: (str: string) => void): bc.DataSubscriber {
return bc.createStackedDataSubscriber(
createValuesPrettyPrinter(indentation, writer),
_comments => {
//onEnd
}
)
}
const parser = new bc.Parser({ allow: bc.lax})
const tokenizer = new bc.Tokenizer(parser)
parser.ondata.subscribe(createPrettyPrinter("\r\n", str => process.stdout.write(str)))
parser.onerror.subscribe(err => { console.error("FOUND PARSER ERROR", err.message) })
tokenizer.onerror.subscribe(err => { console.error("FOUND TOKENIZER ERROR", err.message) })
tokenizer.write(data)
tokenizer.end()
import * as bc from "bass-clarinet"
import * as fs from "fs"
const [, , path] = process.argv
if (path === undefined) {
console.error("missing path")
process.exit(1)
}
const data = fs.readFileSync(path, {encoding: "utf-8"})
const parser = new bc.Parser({ allow: bc.lax})
const tokenizer = new bc.Tokenizer(parser)
parser.ondata.subscribe({
oncomma: () => {
//place your code here
},
oncolon: () => {
//place your code here
},
onlinecomment: (_comment, _range) => {
//place your code here
},
onblockcomment: (_comment, _range, _indent) => {
//indent can be used to strip the leading whitespace of all lines of the block comment.
//indent indicates the indentation string found up to the `/*` characters.
//this is only provided if the block comment starts on a new line
},
onquotedstring: (_value, _quote, _range) => {
//place your code here
//in pure JSON, only '"' is valid for _quote
},
onunquotedtoken: (_value, _range) => {
//place your code here
//in pure JSON, only "null", "true" or "false" are valid for _value
},
onopentaggedunion: _range => {
//place your code here
},
onclosetaggedunion: () => {
//place your code here
},
onoption: (_option, _range) => {
//place your code here
},
onopenarray: (_openCharacterRange, _openCharacter) => {
//place your code here
},
onclosearray: (_closeCharacterRange, _closeCharacter) => {
//place your code here
},
onopenobject: (_startRange, _openCharacter) => {
//place your code here
},
oncloseobject: (_endRange, _closeCharacter) => {
//place your code here
},
onkey: (_key, _range) => {
//place your code here
},
onend: () => {
//place your code here
},
})
parser.onerror.subscribe(err => { console.error("FOUND PARSER ERROR", err.message) })
tokenizer.onerror.subscribe(err => { console.error("FOUND TOKENIZER ERROR", err.message) })
tokenizer.write(data)
tokenizer.end()
pass the following arguments to the parser function. all are optional.
opt
- object bag of settings.
the supported options are:
spaces_per_tab
- number. needed for proper column info.: Rationale: without knowing how many spaces per tab base-clarinet
is not able to determine the colomn of a character. Default is 4
(ofcourse)allow:missing_commas
- boolean. No comma's are required. Rationale: When manually editing documents, keeping track of the comma's is cumbersome. With this option this is no longer an issueallow:trailing_commas
- boolean. allows commas before the }
or the ]
. Rationale: for serializers it is easier to write a comma for every property/element instead of keeping a state that tracks if a property/element is the first one.allow:comments
- boolean. allows both line comments //
and block comments /* */
. Rationale: when using JSON-like documents for editing, it is often useful to add commentsallow:apostrophes_instead_of_quotation_marks
- boolean. Allows '
in place of "
. Rationale: In an editor this is less intrusive (although only slightly)allow:angle_brackets_instead_of_brackets
- boolean. Allows <
and >
in place of [
and ]
. Rationale: a semantic distinction can be made between fixed length arrays (ArrayType
) and variable length arrays (lists
)allow:parens_instead_of_braces
- boolean. Allows (
and )
in place of {
and }
. Rationale: a semantic distinction can be made between objctes with known properties (Type
) and objects with dynamic keys (dictionary
)allow:schema
- boolean. If enabled, the document may start with a !
followed by a value (object
, string
etc). This data can be used by a processor for schema validation. For example a string can indicate a URL of the schema.require:schema
- boolean. see allow:schema
. In this case the schema is required. This option overrides the allow
option.allow:compact
- boolean. At the beginning of a document, after the possible schema, a #
may be placed. This is an indicator for a processor (code that uses bass-clarinet
's API) that the data is compact
. base-clarinet
only sends the compact
flag but does not change any other behaviour. Rationale: If a schema is known, the keys of a Type
are known at design time. these types can therefor be converted to ArrayTypes
and thus omit the keys without losing information. This trades in readability in favor of size. This option indicates that this happened in this document. The file can only be properly interpreted by a processor in combination with the schema.allow:tagged_unions
- boolean. This allows an extra value type that is not present in JSON but is very useful. tagged unions are also known as sum types or choices, see taggedunion. The notation is a pipe, followed by a string, followed by any other value. eg: | "the chosen option" { "my data": "foo" }
. The same information can ofcourse also be written in pure JSON with an array with 2 elements of which the first element is a string.(normalize
and trim
have been dropped as this can equally well be handled in the onsimplevalue handler)
write
- write bytes to the parser. you don't have to do this all at
once. you can keep writing as much as you want.
end
- ends the stream. once ended, no more data may be written, it signals the onend
event.
onerror
- indication that something bad happened. the error will be hanging
out on parser.error
, and must be deleted before parsing can continue. by
listening to this event, you can keep an eye on that kind of stuff. note:
this happens much more in strict mode. argument: instance of Error
.
onsimplevalue
- a simple json value.
onopenobject
- object was opened. this is different from clarinet
as the first key is not treated separately
onkey
- an object key: argument: key, a string with the current key. (Also called for the first key, unlike the behaviour of clarinet
)
oncloseobject
- indication that an object was closed
onopenarray
- indication that an array was opened
onclosearray
- indication that an array was closed
onopentaggedunion
- indication that a tagged union was opened
onoption
- the value of the option (string)
onclosetaggedunion
- indication that a tagged union was closed
onend
- indication that the closed stream has ended.
onready
- indication that the stream has reset.
check issues
everyone is welcome to contribute. patches, bug-fixes, new features
bass-clarinet
git checkout -b my_branch
git push origin my_branch
git clone git://github.com/corno/bass-clarinet.git
FAQs
Unknown package
We found that bass-clarinet demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
Security News
Research
An advanced npm supply chain attack is leveraging Ethereum smart contracts for decentralized, persistent malware control, evading traditional defenses.
Security News
Research
Attackers are impersonating Sindre Sorhus on npm with a fake 'chalk-node' package containing a malicious backdoor to compromise developers' projects.