SRL Archive - Research & Development Lounge

Implementation of Json parser in Simba[LaPe]


CynicRus

2013-10-19 05:49:03

Hello, today I had some free time, and I wrote a small script for Json parsing. Now a lot of services are Json api, I hope this script will help you to work with it.

Requirements:
Simba => 1.0;
Lape interpreter;

Script:


program json;

function CopyStop(const Str: string; const start: Integer; const Stop: Integer): string;
begin
Result := Copy(Str, Start, Stop - Start + 1);
end;

function DynArrayAppend(var V: TStringArray; const R: String): Integer;
begin
Result := Length(V);
SetLength(V, Result + 1);
V[Result] := R;
end;

function JSONEscapeDecode(const JSONData: string): string;
var
ptr: integer = 1;
moji_hex: string;
moji: string = '';
begin
Result := '';
repeat
while (ptr <= length(JSONData)) do
begin
if ptr > length(JSONData) then
Exit;
case JSONData[ptr] of
'\':
begin
if ptr >= length(JSONData) then
Exit;
inc(ptr);
case JSONData[ptr] of
'u':
begin
Inc(ptr);
Moji_Hex := '';
Moji_Hex := Uppercase(Copy(JSONData, ptr, 4));
Moji := '';
Moji := ('$' + Moji_Hex);
Result := Result + Moji;
Inc(ptr, 3);
end;
else
Result := Result + JSONData[ptr];
end;
end;
else
Result := Result + JSONData[ptr];
end;
Inc(ptr);
end;
until ptr >= length(JSONData);
end;

function JSONDataIsList(const JSONData: string): Boolean;
begin
Result := false;
if Length(Trim(JSONData)) = 0 then
exit;
if Trim(JSONData)[1] in ['{', '[', '('] then
Result := True;
end;

function JSONDataIsObject(const JSONData: string): Boolean;
begin
Result := false;
if Length(Trim(JSONData)) = 0 then
exit;
if Trim(JSONData)[1] in ['{'] then
Result := True;
end;

function JSONDataIsArray(const JSONData: string): Boolean;
begin
Result := false;
if Length(Trim(JSONData)) = 0 then
exit;
if Trim(JSONData)[1] in ['['] then
Result := True;
end;

function JSONDataIsNumber(const JSONData: string): Boolean;
var
IndexPOS: Integer;
begin
Result := false;
if Length(Trim(JSONData)) = 0 then
exit;
Result := True;
for IndexPos := 1 to Length(JSONData) do
if not (JSONData[IndexPos] in [' ', '.', 'e', 'E', '-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']) then
begin
Result := False;
Exit;
end;
end;

function JSONDataIsString(const JSONData: string): Boolean;
begin
Result := false;
if Length(Trim(JSONData)) = 0 then
exit;
if Trim(JSONData)[1] in ['"'] then
Result := True;
end;

function JSONDataIsKeyValuePair(const JSONData: string): Boolean;
var
InputPos: Integer = 0;
SeparCount: Integer = 0;
InQuote: Boolean = False;
BraceLevel: Integer = 0;
begin
Result := false;
if Length(Trim(JSONData)) = 0 then
exit;
while (InputPOS < length(JSONData)) do
begin
Inc(InputPos);
case JSONData[InputPos] of
'"':
begin
if InputPos = 1 then
InQuote := not InQuote
else if JSONData[InputPos - 1] <> '\' then
InQuote := not InQuote;
end;
'{': Inc(BraceLevel);
'}': Dec(BraceLevel);
end;
if (not InQuote) and (BraceLevel = 0) then
if JSONData[InputPos] = ':' then
inc(SeparCount);
end;
Result := (SeparCount = 1);
end;

function JSON2TSA(const JSONString: AnsiString; var Decoded: TStringArray): Integer;
const
ListItemSep = ',';

var
Pos: Integer;
NestCount: Integer = 0;
CopyPos: Integer = 1;
TmpStr: string;
InQuote: Boolean = False;
begin
SetLength(Decoded,0);
{ if not JSONDataIsList(JSONString) then
begin
Result := - 1;
Exit;
end; }
for Pos := 1 to Length(JSONString) do
begin
case JSONString[Pos] of
'"':
begin
if pos = 1 then
InQuote := not InQuote
else if JSONString[Pos - 1] <> '\' then
InQuote := not InQuote;
end;
'(', '[', '{':
if not InQuote then
Inc(NestCount);
')', ']', '}':
if not InQuote then
begin
Dec(NestCount);
if NestCount = 0 then
begin
TmpStr := Trim(CopyStop(JSONString, CopyPos + 1, Pos - 1));
// WriteLn(TMPStr);
DynArrayAppend(Decoded,tmpstr);
CopyPos := Pos;
end;
end;
ListItemSep:
if not InQuote then
begin
if NestCount = 1 then
begin
TmpStr := Trim(CopyStop(JSONString, CopyPos + 1, Pos - 1));
// WriteLn(TMPStr);
DynArrayAppend(Decoded,tmpstr);
CopyPos := Pos;
end;
end;
end;
end;
Result := 0;
end;

function JSONGetValueByKey(const JSONList: TStringArray; const Key: string): string;
var
i: integer;
_Key: string;
Len: integer;
DelimiterPos: Integer;
begin
Result := '';
Len := Length(JSONList);
for i := 0 to Len - 1 do
begin
DelimiterPos := Pos(':', JSONList[i]);
if DelimiterPOS > 1 then
begin
_key := copy(JSONList[i], 1, DelimiterPOS - 1);
if _key = key then
begin
Result := CopyStop(JSONList[i], DelimiterPOS + 1, Length(JSONList[i]));
Exit;
end;
end;
end;
end;

function JSONTreeGet(const JSONString: string; const JSONPath: string): string;
var
InputPOS: Integer;
SimpleKeyStartPos: Integer;
ThisKey: string;
JSONList: TSTringArray;
begin
SetLength(JSONList, 1);
if Length(JSONPath) = 0 then
exit;
InputPOS := 0;
SimpleKeyStartPos := 0;
Result := JSONString;
repeat
Inc(InputPOS);
SimpleKeyStartPos := InputPos;
while (InputPOS < length(JSONPath)) and (JSONPath[InputPos] <> ':') do
Inc(InputPos);
if JSONPath[InputPos] <> ':' then
ThisKey := CopyStop(JSONPath, SimpleKeyStartPos, InputPos)
else
ThisKey := CopyStop(JSONPath, SimpleKeyStartPos, InputPos - 1);
if Length(ThisKey) = 0 then
Exit;
JSON2TSA(Result, JSONList);
Result := JSONGetValueByKey(JSONList, ThisKey);
until InputPOS >= Length(JSONPath);
end;

procedure JSONTreeGetMultiple(const JSONString: string; const JSONPath: string; var ValueList: TStringArray);
var
InputPOS: Integer;
SimpleKeyStartPos: Integer;
ThisKey: string;
JSONList: TStringArray;
tmpStr: string;
Found: Boolean;
begin
SetLEngth(JsonList, 1);
if Length(JSONPath) = 0 then
exit;
repeat
Found := false;
InputPOS := 0;
SimpleKeyStartPos := 0;
tmpStr := JSONString;
repeat
Inc(InputPOS);
SimpleKeyStartPos := InputPos;
while (InputPOS < length(JSONPath)) and (JSONPath[InputPos] <> ':') do
Inc(InputPos);
if JSONPath[InputPos] <> ':' then
ThisKey := CopyStop(JSONPath, SimpleKeyStartPos, InputPos)
else
ThisKey := CopyStop(JSONPath, SimpleKeyStartPos, InputPos - 1);
if Length(ThisKey) = 0 then
Exit;
JSON2TSA(tmpStr, JSONList);
tmpStr := JSONGetValueByKey(JSONList, ThisKey);
if (InputPOS = Length(JSONPath)) and (tmpstr <> '') then
begin
DynArrayAppend(ValueList,TmpStr);
tmpStr := '';
Found := True;
end;
until InputPOS >= Length(JSONPath);
until not Found;
end;

function JSONGetKey(const JSONData: string): string;
var
InputPos: integer = 0;
begin
if Length(JSONData) = 0 then
exit;
if JSONData[1] in ['{', '[', '('] then
exit;
while (InputPOS < length(JSONData)) and (JSONData[InputPos] <> ':') do
Inc(InputPos);
case JSONData[InputPos] of
':': Result := Trim(CopyStop(JSONData, 1, InputPos - 1))
else
Result := '';
end;
end;

function JSONGetValue(const JSONData: string): string;
var
InputPos: integer = 0;
begin
Result := 'not set';
if Length(JSONData) = 0 then
exit;
if JSONData[1] in ['{', '[', '('] then
exit;
while (InputPOS < length(JSONData)) and (JSONData[InputPos] <> ':') do
Inc(InputPos);
case JSONData[InputPos] of
':': Result := Trim(CopyStop(JSONData, InputPos + 1, Length(JSONData)))
else
Result := 'notfound';
end;
end;

function DeQuote(const input: string): string;
var
p: integer;
begin
result := input;
p := pos('"', result);
if p > 0 then
result := copy(result, p + 1, length(result) - p);
p := pos('"', result);
if p > 0 then
result := copy(result, 1, p - 1);
end;

function DeBracket(const input: string): string;
var
p: integer;
begin
result := input;
p := pos('[', result);
if p > 0 then
result := copy(result, p + 1, length(result) - p);
p := pos(']', result);
if p > 0 then
result := copy(result, 1, p - 1);
end;

function DeBrace(const input: string): string;
var
p: integer;
begin
result := input;
p := pos('{', result);
if p > 0 then
result := copy(result, p + 1, length(result) - p);
p := pos('}', result);
if p > 0 then
result := copy(result, 1, p - 1);
end;

function DeParen(const input: string): string;
var
p: integer;
begin
result := input;
p := pos('(', result);
if p > 0 then
result := copy(result, p + 1, length(result) - p);
p := pos(')', result);
if p > 0 then
result := copy(result, 1, p - 1);
end;

var
Info: string;
Decoded,Item: TStringArray;
i,j: integer;
key,value: string;
begin
ClearDebug;
info := GetPage('http://services.runescape.com/m=itemdb_rs/bestiary/beastSearch.json?term=sheep+cow');
if JSONDataIsList(info) then
begin
Writeln('Processing as list.');
JSON2TSA(info, Decoded);
Writeln('Entries:', Length(Decoded));
for i := 0 to Length(Decoded) - 1 do
begin
WriteLn(Decoded[i]);
if JSONDataIsList(Decoded[i]) then
begin
Json2TSA(Decoded[i],item);
Writeln('Item Entries:', Length(item));
for j:=0 to length(item)-1 do
if JSONDataIsKeyValuePair(item[j]) then
begin
Writeln('Processing as Key-Value pair.');
value := JSONGetValue(item[j]);
key := JSONGetKey(item[j]);
WriteLn('Key = '+key);
WriteLn('Value = ' + value);
end;
end;
end;
end;
end.

Testing output:

Processing as list.
Entries:24
{"value":42,"label":"Sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 42
Processing as Key-Value pair.
Key = "label"
Value = "Sheep"
{"value":43,"label":"Sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 43
Processing as Key-Value pair.
Key = "label"
Value = "Sheep"
{"value":81,"label":"Cow (6)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 81
Processing as Key-Value pair.
Key = "label"
Value = "Cow (6)"
{"value":397,"label":"Cow (6)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 397
Processing as Key-Value pair.
Key = "label"
Value = "Cow (6)"
{"value":1763,"label":"Sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 1763
Processing as Key-Value pair.
Key = "label"
Value = "Sheep"
{"value":1767,"label":"Cow (6)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 1767
Processing as Key-Value pair.
Key = "label"
Value = "Cow (6)"
{"value":3579,"label":"Sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 3579
Processing as Key-Value pair.
Key = "label"
Value = "Sheep"
{"value":5162,"label":"Sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 5162
Processing as Key-Value pair.
Key = "label"
Value = "Sheep"
{"value":5163,"label":"Sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 5163
Processing as Key-Value pair.
Key = "label"
Value = "Sheep"
{"value":1271,"label":"Golden sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 1271
Processing as Key-Value pair.
Key = "label"
Value = "Golden sheep"
{"value":1272,"label":"Golden sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 1272
Processing as Key-Value pair.
Key = "label"
Value = "Golden sheep"
{"value":1691,"label":"Undead cow (6)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 1691
Processing as Key-Value pair.
Key = "label"
Value = "Undead cow (6)"
{"value":1998,"label":"Plague cow"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 1998
Processing as Key-Value pair.
Key = "label"
Value = "Plague cow"
{"value":1999,"label":"Plague cow"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 1999
Processing as Key-Value pair.
Key = "label"
Value = "Plague cow"
{"value":2310,"label":"Cow calf (2)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 2310
Processing as Key-Value pair.
Key = "label"
Value = "Cow calf (2)"
{"value":2311,"label":"Sheepdog"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 2311
Processing as Key-Value pair.
Key = "label"
Value = "Sheepdog"
{"value":5172,"label":"Golden sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 5172
Processing as Key-Value pair.
Key = "label"
Value = "Golden sheep"
{"value":5173,"label":"Golden sheep"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 5173
Processing as Key-Value pair.
Key = "label"
Value = "Golden sheep"
{"value":5603,"label":"Unicow (82)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 5603
Processing as Key-Value pair.
Key = "label"
Value = "Unicow (82)"
{"value":5986,"label":"Armoured cow thing (96)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 5986
Processing as Key-Value pair.
Key = "label"
Value = "Armoured cow thing (96)"
{"value":6048,"label":"Armoured cow thing (96)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 6048
Processing as Key-Value pair.
Key = "label"
Value = "Armoured cow thing (96)"
{"value":1886,"label":"Cowardly Bandit"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 1886
Processing as Key-Value pair.
Key = "label"
Value = "Cowardly Bandit"
{"value":5097,"label":"Coward in armour (120)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 5097
Processing as Key-Value pair.
Key = "label"
Value = "Coward in armour (120)"
{"value":6049,"label":"Coward with bow (150)"}
Item Entries:2
Processing as Key-Value pair.
Key = "value"
Value = 6049
Processing as Key-Value pair.
Key = "label"
Value = "Coward with bow (150)"
Successfully executed.

As an example, take a sample of Json from the RS Bestiary, but with this script you can work with any json.

Cheers,
Cynic.

Janilabo

2013-10-19 05:57:44

As always, interesting stuff Cynic!
I like it. Good job man.. :)