module watt.json.sax

Parse JSON as a stream.

This parser reads the JSON into memory only as it is parsed.
This makes it more complicated than the watt.json.dom parser, but more flexible.

Code Map

//! Parse JSON as a stream.
module watt.json.sax;


//! Type of a JSON value.
enum Type
{
	//! A JSON null value.
	NULL,
	//! A JSON true or false value.
	BOOLEAN,
	//! A JSON number.
	NUMBER,
	//! A JSON string.
	STRING,
	//! A JSON object, everything between {}.
	OBJECT,
	//! A JSON array, everything between [].
	ARRAY,
}

//! Events which will be produced by get.
enum Event
{
	//! The first event, marks the start of the JSON data.
	START,
	//! The last event, marks the end of the JSON data.
	END,
	//! Event which will occour if invalid JSON is encountered.
	ERROR,
	//! A null was encountered.
	NULL,
	//! A boolean was encountered.
	BOOLEAN,
	//! A number was encountered.
	NUMBER,
	//! A string was encountered.
	STRING,
	//! The start of a JSON object was encountered.
	OBJECT_START,
	//! A JSON object key was encountered (this is a string and still needs to
	//! be unescaped).
	OBJECT_KEY,
	//! The end of a JSON object was encountered.
	OBJECT_END,
	//! The start of a JSON array was encountered.
	ARRAY_START,
	//! The end of a JSON array was encountered.
	ARRAY_END,
}

//! Thrown when an error occurs during building.
class BuilderException : util.JSONException
{
public:
	this(msg: string, location: string) { }
}

//! Parses JSON from a given InputStream.
class SAX
{
public:
	//! Ignore garbage/left over data after the root element is parsed.
	ignoreGarbage: bool;


public:
	//! Creates a JSON object from an InputStream.
	this(source: InputStream, bufferSize: size_t, reallocSize: size_t) { }
	//! Creates a JSON object from an array.
	this(data: const(u8)[]) { }
	//! Creates a JSON object from a string.
	this(data: const(char)[]) { }
	//! Continues parsing the input data and calsl the callback with the
	//! appropriate data.
	fn get(callback: scope (void delegate(Event, const(u8)[]))) { }


protected:
	source: InputStream;
	buffer: u8[];
	reallocSize: size_t;
	current: const(u8)[];
	index: size_t;
	savedMark: size_t;
	isMarked: bool;
	state: ParserStack;
	lastError: string;


protected:
	fn error(message: string, file: string, line: const(i32)) { }
	fn eof() bool { }
	fn mark() { }
	fn retrieve() const(u8)[] { }
	fn getImpl(c: char, skip: bool, advance: bool) bool { }
	fn unget() { }
	fn skipWhite() { }
	fn skipDigits() bool { }
	fn expect(c: char, skip: bool) bool { }
	fn getString(array: const(u8)[]) bool { }
	fn getNumber(array: const(u8)[]) bool { }
	fn getBoolean(array: const(u8)[]) bool { }
	fn getNull(array: const(u8)[]) bool { }
}

//! The main class to build/write JSON.
class Builder
{
public:
	//! Creates a new JSONBuilder object.
	this(output: OutputStream, prettyPrint: bool, indent: const(char)[]) { }
	//! Writes a null value.
	fn buildNull() { }
	//! Writes a number.
	fn buildNumber(number: f64) { }
	//! Writes a number.
	fn buildNumber(number: i32) { }
	//! Writes a number.
	fn buildNumber(number: i64) { }
	//! Writes a string.
	fn buildString(str: const(char)[], escape: bool) { }
	//! Writes a boolean.
	fn buildBoolean(b: bool) { }
	//! Writes the start of a JSON object.
	fn buildObjectStart() { }
	//! Writes the end of a JSON object.
	fn buildObjectEnd() { }
	//! Writes the start of a JSON array.
	fn buildArrayStart() { }
	//! Writes the end of a JSON array.
	fn buildArrayEnd() { }
	//! Finalizes the JSON.
	fn finalize() { }


protected:
	output: OutputStream;
	prettyPrint: bool;
	indent: const(char)[];
	indentLevel: size_t;
	buffer: char[];
	state: ParserStack;


protected:
	fn prepareAndCheck(isString: bool) { }
}

//! Turn an Event into a human readable string.
fn eventToString(event: Event) string { }
class BuilderException : util.JSONException

Thrown when an error occurs during building.

enum Type

Type of a JSON value.

enum NULL

A JSON null value.

enum BOOLEAN

A JSON true or false value.

enum NUMBER

A JSON number.

enum STRING

A JSON string.

enum OBJECT

A JSON object, everything between {}.

enum ARRAY

A JSON array, everything between [].

enum Event

Events which will be produced by get.

enum START

The first event, marks the start of the JSON data.

enum END

The last event, marks the end of the JSON data.

enum ERROR

Event which will occour if invalid JSON is encountered.

enum NULL

A null was encountered.

enum BOOLEAN

A boolean was encountered.

enum NUMBER

A number was encountered.

enum STRING

A string was encountered.

enum OBJECT_START

The start of a JSON object was encountered.

enum OBJECT_KEY

A JSON object key was encountered (this is a string and still needs to be unescaped).

enum OBJECT_END

The end of a JSON object was encountered.

enum ARRAY_START

The start of a JSON array was encountered.

enum ARRAY_END

The end of a JSON array was encountered.

fn eventToString(event: Event) string

Turn an Event into a human readable string.

class SAX

Parses JSON from a given InputStream.

ignoreGarbage: bool

Ignore garbage/left over data after the root element is parsed.

this(source: InputStream, bufferSize: size_t, reallocSize: size_t)

Creates a JSON object from an InputStream.

this(data: const(u8)[])

Creates a JSON object from an array.

this(data: const(char)[])

Creates a JSON object from a string.

fn get(callback: scope (void delegate(Event, const(u8)[])))

Continues parsing the input data and calsl the callback with the appropriate data.

This is the main entrypoint into the SAX parser. The basic idea is that callback will be called with 'events' (triggered by pieces of JSON).

Here's a simple example that parses an entire file from standard input, and does nothing with it.

sax := new Sax(input);  // input is declared in watt.io.std.
loop := true;
fn dgt(event: Event, data: const(u8)[]) {
    loop = event == Event.END || event == Event.ERROR;
}
while (loop) sax.get(dgt);

data is a slice to an internal buffer and will only be valid until the next get call. Strings and numbers still need to be further processed. e.g. through parseDouble and unescapeString.

class Builder

The main class to build/write JSON.

This is the opposite of SAX. Instead of taking input and processing it, this writes out JSON to a given OutputStream.

this(output: OutputStream, prettyPrint: bool, indent: const(char)[])

Creates a new JSONBuilder object.

Parameters

output

The OutputStream to emit to.

prettyPrint

If true emit formatted JSON

indent

: Only relevant if prettyPrint is enabled, sets the indentation per level.

fn buildNull()

Writes a null value.

fn buildNumber(number: f64)

Writes a number.

fn buildNumber(number: i32)

Writes a number.

fn buildNumber(number: i64)

Writes a number.

fn buildString(str: const(char)[], escape: bool)

Writes a string.

If escape is true (default) the string will be escaped, set this to false if you want to write an already escaped strings.

fn buildBoolean(b: bool)

Writes a boolean.

fn buildObjectStart()

Writes the start of a JSON object.

JSON keys are expected to be built with buildString.

fn buildObjectEnd()

Writes the end of a JSON object.

fn buildArrayStart()

Writes the start of a JSON array.

fn buildArrayEnd()

Writes the end of a JSON array.

fn finalize()

Finalizes the JSON.

This is optional but recommended, it checks for malformed JSON and writes an additional newline if prettyPrint is enabled.