JSON Classes
Sparkle provides classes for manipulation JSON representation of data. When receiving or sending client requests and server responses, you can use classes available by Sparkle to easily create or read data in JSON. The following topics provide details about the available classes in Sparkle for JSON manipulation.
JSON Writer
Sparkle provides the TJsonWriter class (declared in unit
Sparkle.Json.Writer.pas
) that allows you to generate JSON-encoded data
to a stream. To use it, just create an instance of TJsonWriter class
passing a TStream object to its constructor method, and then use the
methods available in the class to start encoding data in JSON format.
Data will be saved to the stream.
TJsonWriter class is written to be a lightweight, fast, unidirectional way to generate JSON data.
Creating the writer
JsonWriter := TJsonWriter.Create(MyOutputStream);
Available methods
function WriteBeginArray: TJsonWriter;
function WriteEndArray: TJsonWriter;
Respectively begins a new JSON array, then ends the current array.
function WriteBeginObject: TJsonWriter;
function WriteEndObject: TJsonWriter;
Respectively begins a new JSON object, then ends the current object.
function WriteName(const Name: string): TJsonWriter;
Writes the property name of an JSON object.
function WriteString(const Value: string): TJsonWriter;
Writes a JSON string value. Value will be escaped, if needed.
function WriteRawString(const Value: string): TJsonWriter;
Writers a JSON string value, without escaping. The exact content of Value parameter will be output as a JSON string, so you must be sure escaping is correct.
function WriteBoolean(const Value: boolean): TJsonWriter;
Writes a JSON true or false value.
function WriteNull: TJsonWriter;
Writes a JSON null value.
function WriteDouble(const Value: double): TJsonWriter;
Writes a double value as a JSON number.
function WriteInteger(const Value: Int64): TJsonWriter;
Writes an integer value as a JSON number.
procedure Flush;
Force pending data to be saved in stream.
procedure Close;
Force pending data to be saved in stream and check for inconsistencies in output (for example, an array that was not closed).
Available properties
property IndentLength: integer;
Specifies the length of indentation in characters for the output. Default value is zero which means no indentation or line breaks are added to the output stream, generating a compact representation. If IndentLength is greater then zero, then the output will be indented for a better visual representation of the output.
Remarks
You don't need to call Close method, you can use it just to be sure the JSON representation is correct. You also don't need to call Flush explicitly, if you destroy the writer, all pending data will be flushed automatically. But be aware that data is not written directly to the output stream. An internal cache is used for performance reasons, thus if you need to be sure all written data is in the output stream, always call Flush method for that.
All write methods return the instance of TJsonWriter object, which allows you to use methods using fluent interface, for example:
JsonWriter
.WriteBeginObject
.WriteName('test')
.WriteInteger(12)
.WriteEndObject;
While writing to the stream, the writer might raise errors if the method is called in the incorrect context. For example, if you try ending an array without previously opening an array, or trying to write a property name without previously started an object.
Example
Give the following string with JSON representation:
{
"name": "John",
"count": 562,
"items": [
"one",
true,
null
],
"emptyobject": {},
"object": {
"total": 3.14E-10,
"emptyarray": []
}
}
This is the code used to generate such JSON in the stream specified by AStream variable:
Writer := TJsonWriter.Create(AStream);
Writer
.WriteBeginObject
.WriteName('name')
.WriteString('John')
.WriteName('count')
.WriteInteger(562)
.WriteName('items')
.WriteBeginArray
.WriteString('one')
.WriteBoolean(true)
.WriteNull
.WriteEndArray
.WriteName('emptyobject')
.WriteBeginObject
.WriteEndObject
.WriteName('object')
.WriteBeginObject
.WriteName('total')
.WriteDouble(3.14e-10)
.WriteName('emptyarray')
.WriteBeginArray
.WriteEndArray
.WriteEndObject
.WriteEndObject;
Writer.Free;
JSON Reader
Sparkle provides the TJsonReader class (declared in unit
Sparkle.Json.Reader.pas
) that allows you to read tokens and values from
a stream containing JSON-encoded data. To use it, just create an
instance of TJsonReader class passing a TStream object to its
constructor method, and then use the methods available in the class to
start extracting information from it.
TJsonReader class is written to be a lightweight, fast, unidirectional way to read JSON tokens and values. The tokens are traversed in depth-first order, the same order they appear in the stream.
Creating the reader
JsonReader := TJsonReader.Create(MyInputStream);
TJsonToken enumerated type
type
TJsonToken = (BeginObject, EndObject, BeginArray, EndArray, Name, Boolean, Null, Text, Number, EOF);
Contains all the possible tokens that are extracted by the reader:
- BeginObject and EndObject indicate the begin or end of an object.
- BeginArray and EndArray indicate the begin or end of an array.
- Name indicates it's the property name (in a name/value pair) of an object.
- Boolean and Null refer to JSON boolean (true/false) and JSON null values.
- Text refers to a JSON string.
- Number refers to a JSON number.
- EOF indicates the JSON representation has finished.
Available methods
procedure ReadBeginArray;
procedure ReadEndArray;
Consumes the next token from the stream, ensuring it's BeginArray or EndArray, respectively.
procedure ReadBeginObject;
procedure ReadEndObject;
Consumes the next token from the stream, ensuring it's BeginObject or EndObject, respectively.
function ReadName: string;
Ensures next token is TJsonToken.Name (a property name), consumes it and return its content.
function ReadString: string;
Read the next value as string, consuming the token. If next token is a number, the value will be returned as a string and token will be consumed. If next token is neither Number (JSON number) nor Text (JSON string) an error will be raised. The string return is already unescaped.
function ReadBoolean: boolean;
Ensures next token is TJsonToken.Boolean, consumes it and returns its content as boolean value.
function ReadDouble: double;
Read the next value as double, consuming the token. If next token is a string containing a number representation, the value will be returned as double and token will be consumed. If next token is neither Number (JSON number) or Text (JSON string), nor if next token is Text but contains a value that cannot be converted to a double value, an error will be raised.
function ReadInt64: Int64;
function ReadInteger: integer;
Read the next value as integer (or Int64), consuming the token. If next token is a string containing a number representation, the value will be returned as integer (or Int64) and token will be consumed. If next token is neither Number (JSON number) nor Text (JSON string), or if next token is Text but contains a value that cannot be converted to an integer value, an error will be raised.
procedure SkipValue;
Skips the current value and advances to next token. If the current value is a JSON object or JSON array, it will skip the whole object and array until the token right after the object or array.
procedure ReadNull;
Ensures next token is TJsonToken.Null and consumes it.
function HasNext: boolean;
Returns true if the current array or object has another element (if the array contains another value, or if object contains another name/value pair).
function Peek: TJsonToken;
Returns the type of next token in the stream, without consuming it.
function IsNull: boolean;
Indicates if next token is TJsonToken.Null.
function IsFloat: boolean;
Indicates if next token is a floating point number. This is used to differentiate the number types. If the number fits is an integer (32 or 64 bit), this function will return false.
function IsInt64: boolean;
Indicates if next token is an Int64 type. This is used to differentiate the number types. If the number fits in a 32-bit integer, this function will return false.
function Eof: boolean;
Indicates if next token is TJsonToken.EOF.
Note
All methods might raise errors if they try to consume a token that is not the next token. For example, calling ReadBeginArray will raise an error if the next token is not an array begin ("[" character), at the same time ReadInteger raises an error if next token is not a JSON number with a valid integer value.
Examples
Give the following string with JSON representation:
{
"name": "John",
"count": 562,
"items": [
"one",
true,
null
],
"emptyobject": {},
"object": {
"total": 3.14E-10,
"emptyarray": []
}
}
This is the code used to read such JSON from the stream specified by AStream variable:
Reader := TJsonReader.Create(AStream);
Reader.ReadBeginObject;
Name := Reader.ReadName; // "name"
StrValue := Reader.ReadString; // "John"
Name := Reader.ReadName; // "count"
IntValue := Reader.ReadInteger; // 562
Name := Reader.ReadName; // "items"
Reader.ReadBeginArray;
StrValue := Reader.ReadString; // "one"
BoolValue := Reader.ReadBoolean; // true
Reader.ReadNull; // null
Reader.ReadEndArray;
Name := Reader.ReadName; // "emptyobject"
Reader.ReadBeginObject;
Reader.ReadEndObject;
Name := Reader.ReadName; // "object"
Reader.ReadBeginObject;
Name := Reader.ReadName; // "total"
DoubleValue := Reader.ReadDouble; // 3.14E-10
Name := Reader.ReadName; // "emptyarray"
Reader.ReadBeginArray;
Reader.ReadEndArray;
Reader.ReadEndObject;
Reader.ReadEndObject;
Reader.Free;
The following example shows how to use HasNext function to iterate through array values or object properties, and how to use Peek to know what the type of next token and perform actions based on that.
Reader.ReadBeginObject;
while Reader.HasNext do
begin
PropertyName := Reader.ReadName;
case Reader.Peek of
TJsonToken.Text: StrValue := Reader.ReadString;
TJsonToken.Number: DoubleValue := Reader.ReadNumber;
TJsonToken.Boolean: BooleanValue := Reader.ReadBoolean;
TJsonToken.Null: Reader.ReadNull;
TJsonToken.BeginObject: ProcessObject;
TJsonToken.BeginArray: ProcessArray;
end;
end;
Reader.ReadEndObject;