Class json

Introduction

This class provides methods for parsing and generating JavaScript Object Notation (JSON).

Contents

  1. Introduction
  2. Contents
  3. Explanation of symbols used
  4. Member reference
    1. (constructors)
    2. AddMapper - add a mapper
    3. applyExponent - convert base and exponent to a value.
    4. Default - a default json instance
    5. emit - Generate JSON from an object
    6. escape - Escapes a string value for JSON
    7. parse - Parse JSON
    8. unescape - Unescapes a JSON string value
  5. Supporting classes
    1. Bool - a JSON true/false
    2. Mapper - a JSON <-> object mapper
    3. DefaultMapper - a default mapper
    4. Null - stand-in for ^null
    5. ParserException - thrown on a parse error

Explanation of symbols used

Words in italics indicate an instance of a class. The word corresponds to the class name, except where more than one instance is represented in the same statement. In that case a number (2, 3, etc.) is appended to the class name.

Words in normal typeface are to be taken literally (required punctuation, class name in a static reference, method name, etc.)

The symbol => is used to separate an expression (on the left) from its return value (on the right).

An ellipsis (...) indicates that the previous argument may be repeated any number of times. The description will indicate whether one instance is required.

Member reference

constructors

new json() => json
new json(mapper) => json
Creates a new JSON parser/emitter. If mapper is passed, it will become the primary JSON<->object mapper, and its Next member will be set to the DefaultMapper instance. If mapper is not passed, then the DefaultMapper instance will become the primary mapper.

method AddMapper

json.AddMapper(mapper)
This method makes mapper the primary object mapper for json, and sets mapper's Next member to the previous primary mapper.

static method applyExponent

json.applyExponent(decimal, int) => decimal
This static method takes a base and exponent (power of 10) and returns a normal decimal value. It can be useful in an override for numberValue when the exponent cannot be retained in the storage for the number. Be aware, however, that this method can result in an overflow or underflow of the available storage for decimal. In either case, the result will be zero.

static property Default

json.Default => json
This property yields an instance of json that uses the DefaultMapper as its primary mapper. It is functionally equivalent to new json(), but is provided for the convenience of using it where the "new" keyword is not syntactically valid.

method emit

json.emit(object) => string

Produces a string representing object as JSON, using json's Mappers.

static method escape

json.escape(a) => string

Escapes special characters within a for use in JSON, returning the fully escaped string. The following characters are escaped:

CharacterHexadecimalEscape sequence
BS08\b
TAB09\t
LF0A\n
FF0C\f
CR0D\r
"22\"
/2F\/
\5C\\
Other control characters, and DEL00-07,0B,0E-1F,7F\u00XX, where XX is the hexadecimal character value

You do not need to call this routine before calling emit. It will be called automatically when generating string values and object names. If you override emit in a Mapper, however, then you will need to call escape for any object name or string value you generate.

method parse

json.parse(a) => object

Parses the JSON contained in a and produces a Synergy/DE object that represents it, based on json's Mappers.

If the parser encounters syntax that isn't recognized as valid JSON, a ParserException will be thrown.

static method unescape

json.unescape(a) => string

Replaces JSON escape sequences in a with their proper characters, returning the converted string. The escapes replaced match those listed under the escape method. Note that Unicode escapes for character values greater than 127 (hex 7F) will not result in the desired character. Synergy/DE does not use Unicode, so only the low-order 8 bits will be treated as ASCII instead.

You do not need to call this method in conjunction with parse. It will be called automatically for string values and object names.

Class Bool

new json.Bool(boolean) => bool

This class wraps boolean values for use in JSON, because booleans in Synergy/DE are easily confused with integers. The DefaultMapper's emit method will emit a Bool as either true or false, rather than its numeric value. Conversely, the parse method will create a Bool when it encounters true or false as a value, so you can tell that the value was boolean rather than a number. Use (obj .is. json.Bool) to determine that it is a Bool.

This class takes its truth value as the only argument to its constructor. It mixes in boolean so you can use the object in any test for truth or falsehood, without casting it. It also mixes in comparable, so you can compare a Bool against another Bool or a boolean. Furthermore, Bool overrides object.Equals to compare equality with Bool, Var (using Var's truth value), or @boolean.

This class overrides object.ToString() to generate "true" or "false".

abstract class Mapper

This class is the base for JSON <-> object mappers. A Mapper converts JSON to Synergy/DE objects and vice versa. A default mapper is provided by the DefaultMapper class.

If you derive your own Mapper, you do not need to provide mapping for all objects. Any method that is not overridden invokes the corresponding method on its Next member. Likewise, if your method does not know how to handle a particular object, it can call the parent method to invoke the method on the next Mapper. This allows you to add more than one Mapper to a json parser/emitter to handle different classes of object. By default, the last Mapper in the chain is the DefaultMapper.

The Mapper class has the following public members:

  1. addElement - add an element to an array
  2. addPair - add a pair to an object
  3. booleanValue - create a boolean
  4. createArray - create an array
  5. createObject - create an object
  6. emit - emit an object as JSON
  7. Next - next Mapper in chain
  8. nullValue - create a null
  9. numberValue - create a number
  10. stringValue - create a string

virtual method addElement

mapper.addElement(object, object2)
This method adds object2 to the end of the array represented by object. A derived class should implement this method if and only if it also implements createArray, because object is the object returned by a prior call to that method. The base class calls Next.addElement(object, object2)

virtual method addPair

mapper.addPair(object, a, object2)
This method adds object2 to the JSON object represented by object, using the name a. A derived class should implement this method if and only if it also implements createObject, because object is the object returned by a prior call to that method. The base class calls Next.addPair(object, a, object2)

virtual method booleanValue

mapper.booleanValue(boolean) => object
This method must produce an object that represents a JSON true or false value. It is called whenever the json parse method encounters a true or false. The argument boolean contains true or false. If a derived class does not wish to override the DefaultMapper representation of boolean values, it should not implement this method. The base class calls Next.booleanValue(boolean)

virtual method createArray

mapper.createArray() => object
This method must create an object that represents a JSON array. It is called whenever the json parse method encounters the JSON token "[". If a derived class does not wish to override the DefaultMapper handling of arrays, that class should not implement this method. The base class calls Next.createArray()

virtual method createObject

mapper.createObject() => object
This method must create an object that represents a JSON object. It is called whenever the json parse method encounters the JSON token "{". If a derived class does not wish to override the DefaultMapper handling of objects, that class should not implement this method. The base class calls Next.createObject()

virtual method emit

mapper.emit(json, object) => string

This method must produce a string containing the JSON that represents object. If a Mapper does not wish to assume responsibility for generating JSON for the class of object, it should call parent.emit(json, object). If the emission includes other objects (i.e., if this object represents a JSON object or array), then to generate the JSON for those component objects it should call json.emit(object2), rather than generating it inline or recursing to this method. That allows any mapper that has been inserted before this one to have the first crack at generating each object's JSON.

The base class calls Next.emit(json, object)

Next

mapper.Next => mapper2
mapper.Next = mapper2
This member specifies the next Mapper in the chain of Mappers. By default, the last Mapper in the chain will be the DefaltMapper, but Next may be set to ^null if you override all Mapper methods and never invoke a method on Next.

virtual method nullvalue

mapper.nullValue() => object
This method must produce an object (or ^null) that represents a JSON null value. We do not assume that null should always be represented as ^null, chiefly because ^null in a Hash is treated as "not specified". If a derived class does not wish to override the DefaultMapper representation of null values, then it should not implement this method. The base class calls Next.nullValue()

virtual method numberValue

mapper.numberValue(decimal, int) => object
This method must produce an object that represents a JSON number value. It is called whenever the json parse method encounters a number value. The argument decimal contains the base numeric value, and int contains the power of 10 (zero if none specified). To apply the exponent and produce only a decimal value, call json.applyExponent(decimal, int), which may result in an overflow or underflow. This method provides both arguments so you have the option of storing greater precision than decimal affords. If you do not wish to override the DefaultMapper representation of number values, you should not implement this method. The base class calls Next.numberValue(decimal, int)

virtual method stringValue

mapper.stringValue(a) => object
This method must produce an object that represents a JSON string value. It is called whenever the json parse method encounters a string value other than an object name. The argument a contains the unquoted, unescaped value of the string. If a derived class does not wish to override the DefaultMapper representation of string values, it should not implement this method. The base class calls Next.stringValue(a)

Class DefaultMapper extends Mapper

This class provides default JSON <-> object mapping. Its emit method translates the following Synergy/DE classes to JSON components:

Legend
Not available on .NET
Available on all platforms

Synergy/DE classJSON component
^nullnull
ArrayList (includes ls)array
@booleantrue/false
@bytenumber
@dnumber
@decimalnumber
Hashobject
@inumber
@intnumber
json.Booltrue/false
json.Nullnull
@longnumber
@sbytenumber
@shortnumber
VarDecnumber
VarIntnumber
anything elsestring (using Object.ToString())

Where a class is listed as "not available on .NET" that is because we cannot interrogate those types in Synergy/DE on .NET, so they are converted to strings. For cross-platform code, stick to the options that are available on all platforms.

Null presents another interesting case. You can have an ArrayList member that is ^null, but setting a Hash member to ^null is equivalent to deleting it. So if you want the JSON to specify null for the value of a pair in an object, use json.Null.instance instead of ^null.

Hashes are emitted as objects in {}, specifying key/value pairs as "name": value, separated by commas (with a maximum name length of 1024 characters). The emit method calls json.emit for each value, so if you provide your own mapper you can still use this mapper to represent objects as Hashes while doing your own mapping for other classes if you choose. Hashes should be CaseSensitive, or every name will be uppercase.

ArrayLists are emitted as arrays in [], with each member emitted as a value, separated by commas. The emit method calls json.emit for each value.

JSON elements map to Synergy/DE objects as follows:

JSON componentSynergy/DE class
arrayls
falsejson.Bool
nulljson.Null
numberVarDec
objectHash
stringVarAlpha
truejson.Bool

DefaultMapper uses the cross-platform compatible options for numbers and boolean values. Null also translates to json.Null instead of ^null, because otherwise you would not be able to see it in a Hash. Hashes created by DefaultMapper are always case-sensitive.

Class Null

json.Null.instance => null

This class is used to stand in for ^null, because Hash members that are null are treated as unspecified. To test whether an object is a Null, use the Synergy/DE .is. operator: (obj .is. json.Null).

This class mixes in the Singleton pattern, so there is only one instance. You can access it as json.Null.instance.

Class ParserException extends Synergex.SynergyDE.SynException

This class of exception will be thrown whenever the parse or unescape methods encounter invalid JSON syntax. The Message property will begin with "JSON Parsing error: ", followed by a more detailed description:

MessageExplanation
colon expectedAn object name was not followed by a colon
Exponent expectedA number was followed by e or E, but nothing else
Four digits required for Unicode escape\u was followed by less than four characters
Invalid numeric formatA number contained a character that was not 0-9, ., e, E, + or -, or it contained more than one e or E
string must begin with "An object name (within {}) did not begin with a quote
Unescaped \ at end of stringA string value or object name ended with '\'.
Unexpected end of stringThe end of the string was reached before all required tokens
Unrecognized hex unicode valueThe four characters following \u were not all valid hex digits (0-9,A-F,a-f)
Unrecognized valueA value did not begin with {, [, ", -, or 0-9, and it was not true, false, or null.