I need to consume a REST API that returns a JSON that resembles the following:
[
{
"purchaseCode": 1,
"totalItems": 5,
"totalPrice": 5102.04,
"deliveryAddress": {
"name": "Michael Jennette",
"country": "Brazil",
"state": "São Paulo",
"postCode": "16",
"landmark": ""
}
},
{
"purchaseCode": 2,
"totalItems": 3,
"totalPrice": 4312.65,
"deliveryAddress": {
"name": "David Samuel",
"country": "Brazil",
"state": "São Paulo",
"postCode": "40",
"landmark": ""
}
}
]
I can easily convert this JSON format to a class using TJSONObject.ParseJSONValue. The problem is that I need to print reports with this json's data. So it's more interesting to convert JSON to DataSet.
Before JSON existed, XML was heavily used. In XMLs that an object owned another object, the DataSet had to have a field of type DataSet that would be referenced by another DataSet. This way it is possible to convert all XML to DataSet.
I'm trying to do the same with JSON using the native component TRESTResponseDataSetAdapter. I have seen that I can specify the fields for ResponseDataSetAdapter and that each field can have "child fields".
Therefore, I defined "deliveryAddress" as the DataSet type and specified its "child fields". But when I run the request, I get an "Invalid Argument" error and then another "Invalid value for field "deliveryAddress" error.
Is it possible to convert this JSON format to DataSet using TRESTResponseDataSetAdapter? If yes, how? Or is there another way? I did not want to have to convert to classes and then manually fill each DataSet.
Sample project:
Server (WebBroker)
Project1.dpr
program Project1;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Types,
IPPeerServer,
IPPeerAPI,
IdHTTPWebBrokerBridge,
Web.WebReq,
Web.WebBroker,
WebModuleUnit1 in 'WebModuleUnit1.pas' {WebModule1: TWebModule},
ServerConst1 in 'ServerConst1.pas';
{$R *.res}
function BindPort(Aport: Integer): Boolean;
var
LTestServer: IIPTestServer;
begin
Result := True;
try
LTestServer := PeerFactory.CreatePeer('', IIPTestServer) as IIPTestServer;
LTestServer.TestOpenPort(APort, nil);
except
Result := False;
end;
end;
function CheckPort(Aport: Integer): Integer;
begin
if BindPort(Aport) then
Result := Aport
else
Result := 0;
end;
procedure SetPort(const Aserver: TIdHTTPWebBrokerBridge; APort: String);
begin
if not (Aserver.Active) then
begin
APort := APort.Replace(cCommandSetPort, '').Trim;
if CheckPort(APort.ToInteger) > 0 then
begin
Aserver.DefaultPort := APort.ToInteger;
Writeln(Format(sPortSet, [APort]));
end
else
Writeln(Format(sPortInUse, [Aport]));
end
else
Writeln(sServerRunning);
Write(cArrow);
end;
procedure StartServer(const Aserver: TIdHTTPWebBrokerBridge);
begin
if not (Aserver.Active) then
begin
if CheckPort(Aserver.DefaultPort) > 0 then
begin
Writeln(Format(sStartingServer, [Aserver.DefaultPort]));
Aserver.Bindings.Clear;
Aserver.Active := True;
end
else
Writeln(Format(sPortInUse, [Aserver.DefaultPort.ToString]));
end
else
Writeln(sServerRunning);
Write(cArrow);
end;
procedure StopServer(const Aserver: TIdHTTPWebBrokerBridge);
begin
if Aserver.Active then
begin
Writeln(sStoppingServer);
Aserver.Active := False;
Aserver.Bindings.Clear;
Writeln(sServerStopped);
end
else
Writeln(sServerNotRunning);
Write(cArrow);
end;
procedure WriteCommands;
begin
Writeln(sCommands);
Write(cArrow);
end;
procedure WriteStatus(const Aserver: TIdHTTPWebBrokerBridge);
begin
Writeln(sIndyVersion + Aserver.SessionList.Version);
Writeln(sActive + Aserver.Active.ToString(TUseBoolStrs.True));
Writeln(sPort + Aserver.DefaultPort.ToString);
Writeln(sSessionID + Aserver.SessionIDCookieName);
Write(cArrow);
end;
procedure RunServer(APort: Integer);
var
LServer: TIdHTTPWebBrokerBridge;
LResponse: string;
begin
WriteCommands;
LServer := TIdHTTPWebBrokerBridge.Create(nil);
try
LServer.DefaultPort := APort;
while True do
begin
Readln(LResponse);
LResponse := LowerCase(LResponse);
if LResponse.StartsWith(cCommandSetPort) then
SetPort(LServer, LResponse)
else if sametext(LResponse, cCommandStart) then
StartServer(LServer)
else if sametext(LResponse, cCommandStatus) then
WriteStatus(LServer)
else if sametext(LResponse, cCommandStop) then
StopServer(LServer)
else if sametext(LResponse, cCommandHelp) then
WriteCommands
else if sametext(LResponse, cCommandExit) then
if LServer.Active then
begin
StopServer(LServer);
break
end
else
break
else
begin
Writeln(sInvalidCommand);
Write(cArrow);
end;
end;
finally
LServer.Free;
end;
end;
begin
try
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass := WebModuleClass;
RunServer(8080);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end
end.
ServerConst1.pas
unit ServerConst1;
interface
resourcestring
sPortInUse = '- Error: Port %s already in use';
sPortSet = '- Port set to %s';
sServerRunning = '- The Server is already running';
sStartingServer = '- Starting HTTP Server on port %d';
sStoppingServer = '- Stopping Server';
sServerStopped = '- Server Stopped';
sServerNotRunning = '- The Server is not running';
sInvalidCommand = '- Error: Invalid Command';
sIndyVersion = '- Indy Version: ';
sActive = '- Active: ';
sPort = '- Port: ';
sSessionID = '- Session ID CookieName: ';
sCommands = 'Enter a Command: ' + slineBreak +
' - "start" to start the server'+ slineBreak +
' - "stop" to stop the server'+ slineBreak +
' - "set port" to change the default port'+ slineBreak +
' - "status" for Server status'+ slineBreak +
' - "help" to show commands'+ slineBreak +
' - "exit" to close the application';
const
cArrow = '->';
cCommandStart = 'start';
cCommandStop = 'stop';
cCommandStatus = 'status';
cCommandHelp = 'help';
cCommandSetPort = 'set port';
cCommandExit = 'exit';
implementation
end.
WebModuleUnit1.pas
unit WebModuleUnit1;
interface
uses System.SysUtils, System.Classes, Web.HTTPApp;
type
TWebModule1 = class(TWebModule)
procedure WebModule1jsonAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
var
WebModuleClass: TComponentClass = TWebModule1;
implementation
{%CLASSGROUP 'System.Classes.TPersistent'}
{$R *.dfm}
procedure TWebModule1.WebModule1jsonAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.ContentType := 'application/json; charset=utf-8';
Response.Content :=
'[{"purchaseCode":1,"totalItems":5,"totalPrice":5102.04,"deliveryAddress":{"name":"Michael Jennette","country":"Brazil","state":"S\u00E3o Paulo","postCode":"16","landmark":""}},' +
'{"purchaseCode":2,"totalItems":3,"totalPrice":4312.65,"deliveryAddress":{"name":"David Samuel","country":"Brazil","state":"S\u00E3o Paulo","postCode":"40","landmark":""}}]';
end;
end.
WebModuleUnit1.dfm
object WebModule1: TWebModule1
OldCreateOrder = False
Actions = <
item
MethodType = mtGet
Name = 'json'
PathInfo = '/json'
OnAction = WebModule1jsonAction
end>
Height = 230
Width = 415
end
Client
Client1.dpr
program Client1;
uses
Vcl.Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Unit1.pas
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, IPPeerClient, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param,
FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.Comp.DataSet, FireDAC.Comp.Client,
REST.Response.Adapter, REST.Client, Data.Bind.Components, Data.Bind.ObjectScope, Vcl.Grids, Vcl.DBGrids, Vcl.StdCtrls;
type
TForm1 = class(TForm)
btnRequest: TButton;
DBGrid1: TDBGrid;
DataSource1: TDataSource;
RESTClient1: TRESTClient;
RESTRequest1: TRESTRequest;
RESTResponse1: TRESTResponse;
RESTResponseDataSetAdapter1: TRESTResponseDataSetAdapter;
FDMemTable1: TFDMemTable;
FDMemTable2: TFDMemTable;
FDMemTable1purchaseCode: TIntegerField;
FDMemTable1totalItems: TIntegerField;
FDMemTable1totalPrice: TCurrencyField;
FDMemTable1deliveryAddress: TDataSetField;
FDMemTable2name: TStringField;
FDMemTable2country: TStringField;
FDMemTable2state: TStringField;
FDMemTable2postCode: TStringField;
FDMemTable2landmark: TStringField;
procedure btnRequestClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnRequestClick(Sender: TObject);
begin
RESTRequest1.Execute;
end;
end.
Unit1.dfm
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 362
ClientWidth = 724
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object btnRequest: TButton
Left = 24
Top = 24
Width = 75
Height = 25
Caption = 'Request'
TabOrder = 0
OnClick = btnRequestClick
end
object DBGrid1: TDBGrid
Left = 296
Top = 48
Width = 409
Height = 297
DataSource = DataSource1
TabOrder = 1
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
end
object DataSource1: TDataSource
DataSet = FDMemTable1
Left = 232
Top = 152
end
object RESTClient1: TRESTClient
Accept = 'application/json, text/plain; q=0.9, text/html;q=0.8,'
AcceptCharset = 'UTF-8, *;q=0.8'
BaseURL = 'http://localhost:8080/json'
Params = <>
HandleRedirects = True
RaiseExceptionOn500 = False
Left = 32
Top = 64
end
object RESTRequest1: TRESTRequest
Client = RESTClient1
Params = <>
Response = RESTResponse1
SynchronizedEvents = False
Left = 32
Top = 112
end
object RESTResponse1: TRESTResponse
ContentType = 'application/json'
Left = 32
Top = 160
end
object RESTResponseDataSetAdapter1: TRESTResponseDataSetAdapter
Dataset = FDMemTable1
FieldDefs = <
item
Name = 'purchaseCode'
DataType = ftInteger
end
item
Name = 'totalItems'
DataType = ftInteger
end
item
Name = 'totalPrice'
DataType = ftCurrency
end
item
Name = 'deliveryAddress'
ChildDefs = <
item
Name = 'name'
DataType = ftString
Size = 150
end
item
Name = 'country'
DataType = ftString
Size = 150
end
item
Name = 'state'
DataType = ftString
Size = 150
end
item
Name = 'postCode'
DataType = ftString
Size = 50
end
item
Name = 'landmark'
DataType = ftString
Size = 150
end>
DataType = ftDataSet
Size = 5
end>
Response = RESTResponse1
Left = 32
Top = 216
end
object FDMemTable1: TFDMemTable
FetchOptions.AssignedValues = [evMode]
FetchOptions.Mode = fmAll
ResourceOptions.AssignedValues = [rvSilentMode]
ResourceOptions.SilentMode = True
UpdateOptions.AssignedValues = [uvCheckRequired]
UpdateOptions.CheckRequired = False
Left = 168
Top = 152
object FDMemTable1purchaseCode: TIntegerField
FieldName = 'purchaseCode'
end
object FDMemTable1totalItems: TIntegerField
FieldName = 'totalItems'
end
object FDMemTable1totalPrice: TCurrencyField
FieldName = 'totalPrice'
end
object FDMemTable1deliveryAddress: TDataSetField
FieldName = 'deliveryAddress'
end
end
object FDMemTable2: TFDMemTable
FetchOptions.AssignedValues = [evMode]
FetchOptions.Mode = fmAll
ResourceOptions.AssignedValues = [rvSilentMode]
ResourceOptions.SilentMode = True
UpdateOptions.AssignedValues = [uvCheckRequired]
UpdateOptions.CheckRequired = False
Left = 168
Top = 224
object FDMemTable2name: TStringField
FieldName = 'name'
Size = 150
end
object FDMemTable2country: TStringField
FieldName = 'country'
Size = 150
end
object FDMemTable2state: TStringField
FieldName = 'state'
Size = 150
end
object FDMemTable2postCode: TStringField
FieldName = 'postCode'
Size = 50
end
object FDMemTable2landmark: TStringField
FieldName = 'landmark'
Size = 150
end
end
end
Related
I'm making an indicator that plots the last 5 candles of each user-selected timeframe. Everything else is working (i think lol) besides my scaling/normalize function (it's messing up the wicks/high and low values) and my _lastDiffValue function.
I'm trying to create a function (_lastDiffValue) that runs a while loop and returns a specific integer for a history reference. The integer would be how many times the while loop ran. This way I can find the history reference for a specific condition. The reason I'm trying to do this is because the series I am working with has a lot of repetitive values and I want to find the last different value and assign it to a variable. This is one piece of a very involved indicator (at least for my experience lol).
Here's the code:
// © pipjitsu
//#version=5
indicator("Multi-TF Candles", overlay = false, explicit_plot_zorder = true)
group1 = "Timeframes"
bull_col = input.color(color.blue, "Bullish")
bear_col = input.color(color.new(color.red, 0), "Bearish")
wick_color = input.color(#000000, "Wick Color")
lookback = input.int(6, "Candles per Timeframe")
space = 3
tf1 = input.timeframe("1D", title = "", inline = "Column 1", group = group1)
tf2 = input.timeframe("240", title = "", inline = "Column 1", group = group1)
tf3 = input.timeframe("60", title = "", inline = "Column 1", group = group1)
tf4 = input.timeframe("15", title = "", inline = "Column 1", group = group1)
tf5 = input.timeframe("5", title = "", inline = "Column 1", group = group1)
tf6 = input.timeframe("1", title = "", inline = "Column 1", group = group1)
_makeCandle(_open, _high, _low, _close, _group) =>
if barstate.islast
for i = 1 to lookback
_bias = _close[i] > _open[i] ? bull_col : bear_col
_shift = (i * space) + ((_group - 1) * (space * lookback + 4))
line1 = line.new(bar_index - _shift, _high[i], bar_index - _shift, _low[i], color = wick_color)
line.delete(line1[1])
box1 = box.new((bar_index - 1) - _shift, _open[i], (bar_index + 1) - _shift, _close[i], bgcolor = _bias, border_color = color.black)
box.delete(box1[1])
_lastDiffVal(_val) =>
var float _newVal = _val + 1
var int counter = na
while _val == _newVal
counter := counter + 1
_newVal := _val[counter]
counter
_normalize(_src) =>
_min = 0
_max = 100
var _historicMin = 10e10
var _historicMax = -10e10
_historicMin := math.min(nz(_src, _historicMin), _historicMin)
_historicMax := math.max(nz(_src, _historicMax), _historicMax)
_min + (_max - _min) * (_src - _historicMin) / math.max(_historicMax - _historicMin, 10e-10)
_getData(_tf) =>
[_open, _high, _low, _close] = request.security("", _tf, [open[_lastDiffVal(open)], high[_lastDiffVal(high)], low[_lastDiffVal(low)], close[_lastDiffVal(close)]])
_open := _normalize(_open)
_high := _normalize(_high)
_low := _normalize(_low)
_close := _normalize(_close)
[_open, _high, _low, _close]
_masterFun(_tf, _group) =>
[_open, _high, _low, _close] = _getData(_tf)
_makeCandle(_open, _high, _low, _close, _group)
_masterFun(tf1, 1)
_masterFun(tf2, 2)
_masterFun(tf3, 3)
_masterFun(tf4, 4)
_masterFun(tf5, 5)
_masterFun(tf6, 6)
I am using Lua version 54 inside my windows 64 bit OS. I am struggling writing the proper syntax for opening a directory, then opening the local file with path. Then once the file is open (JSON File) converting it to Lua Table.
Code I am using:
Lua54 syntax
local archhudbasic = io.open("C:\\Users\\Lichr\\Documents",)
Internal functions
local function kind_of(obj)
if type(obj) ~= 'table' then return type(obj) end
local i = 1
for _ in pairs(obj) do
if obj[i] ~= nil then i = i + 1 else return 'table' end
end
if i == 1 then return 'table' else return 'array' end
end
local function escape_str(s)
local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'}
local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'}
for i, c in ipairs(in_char) do s = s:gsub(c, '\\' .. out_char[i])
end
return s
end
Returns pos, did_find; there are two cases:
Delimiter found: pos = pos after leading space + delim; did_find = true.
Delimiter not found: pos = pos after leading space; did_find = false.
This throws an error if err_if_missing is true and the delim is not found.
local function skip_delim(str, pos, delim, err_if_missing)
pos = pos + #str:match('^%s*', pos)
if str:sub(pos, pos) ~= delim then
if err_if_missing then
error('Expected ' .. delim .. ' near position ' .. pos)
end
return pos, false
end
return pos + 1, true
end
Expects the given pos to be the first character after the opening quote.
Returns val, pos; the returned pos is after the closing quote character.
local function parse_str_val(str, pos, val)
val = val or ''
local early_end_error = 'End of input found while parsing string.'
if pos > #str then error(early_end_error) end
local c = str:sub(pos, pos)
if c == '"' then return val, pos + 1 end
if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end
-- We must have a \ character.
local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
local nextc = str:sub(pos + 1, pos + 1)
if not nextc then error(early_end_error) end
return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc))
end
Returns val, pos; the returned pos is after the number's final character.
local function parse_num_val(str, pos)
local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
local val = tonumber(num_str)
if not val then error('Error parsing number at position ' .. pos .. '.') end
return val, pos + #num_str
end
Public values and functions.
function json.stringify(obj, as_key)
local s = {} -- We'll build the string as an array of strings to be concatenated.
local kind = kind_of(obj) -- This is 'array' if it's an array or type(obj)
otherwise.
if kind == 'array' then
if as_key then error('Can\'t encode array as key.') end
s[#s + 1] = '['
for i, val in ipairs(obj) do
if i > 1 then s[#s + 1] = ', ' end
s[#s + 1] = json.stringify(val)
end
s[#s + 1] = ']'
elseif kind == 'table' then
if as_key then error('Can\'t encode table as key.') end
s[#s + 1] = '{'
for k, v in pairs(obj) do
if #s > 1 then s[#s + 1] = ', ' end
s[#s + 1] = json.stringify(k, true)
s[#s + 1] = ':'
s[#s + 1] = json.stringify(v)
end
s[#s + 1] = '}'
elseif kind == 'string' then
return '"' .. escape_str(obj) .. '"'
elseif kind == 'number' then
if as_key then return '"' .. tostring(obj) .. '"' end
return tostring(obj)
elseif kind == 'boolean' then
return tostring(obj)
elseif kind == 'nil' then
return 'null'
else
error('Unjsonifiable type: ' .. kind .. '.')
end
return table.concat(s)
end
json.null = {} //This is a one-off table to represent the null value.
function json.parse(str, pos, end_delim)
pos = pos or 1
if pos > #str then error('Reached unexpected end of input.') end
local pos = pos + #str:match('^%s*', pos) -- Skip whitespace.
local first = str:sub(pos, pos)
if first == '{' then -- Parse an object.
local obj, key, delim_found = {}, true, true
pos = pos + 1
while true do
key, pos = json.parse(str, pos, '}')
if key == nil then return obj, pos end
if not delim_found then error('Comma missing between object items.') end
pos = skip_delim(str, pos, ':', true) -- true -> error if missing.
obj[key], pos = json.parse(str, pos)
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '[' then -- Parse an array.
local arr, val, delim_found = {}, true, true
pos = pos + 1
while true do
val, pos = json.parse(str, pos, ']')
if val == nil then return arr, pos end
if not delim_found then error('Comma missing between array items.') end
arr[#arr + 1] = val
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '"' then -- Parse a string.
return parse_str_val(str, pos + 1)
elseif first == '-' or first:match('%d') then -- Parse a number.
return parse_num_val(str, pos)
elseif first == end_delim then -- End of an object or array.
return nil, pos + 1
else -- Parse true, false, or null.
local literals = {['true'] = true, ['false'] = false, ['null'] = json.null}
for lit_str, lit_val in pairs(literals) do
local lit_end = pos + #lit_str - 1
if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
end
local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
error('Invalid json syntax starting at ' .. pos_info_str)
end
end
return json
I am new to lua coding. What am I missing? How can I make this code functional inside the terminal?
I need to read data in my query with utf8 format, I tried to change collation of my SQL database when I read data base on English alphabet every thing good, but I have trouble in Arabic or other languages.
I print a string stored in variable came from in mysql query and show me like this ???????
how I can solve this problem to show them correct?
After retrieving UTF-8 strings from database, you should manually convert them to CP1256.
You can use function str:fromutf8() defined below
local char, byte, pairs, floor = string.char, string.byte, pairs, math.floor
local table_insert, table_concat = table.insert, table.concat
local unpack = table.unpack or unpack
local function unicode_to_utf8(code)
-- converts numeric UTF code (U+code) to UTF-8 string
local t, h = {}, 128
while code >= h do
t[#t+1] = 128 + code%64
code = floor(code/64)
h = h > 32 and 32 or h/2
end
t[#t+1] = 256 - 2*h + code
return char(unpack(t)):reverse()
end
local function utf8_to_unicode(utf8str, pos)
-- pos = starting byte position inside input string (default 1)
pos = pos or 1
local code, size = utf8str:byte(pos), 1
if code >= 0xC0 and code < 0xFE then
local mask = 64
code = code - 128
repeat
local next_byte = utf8str:byte(pos + size) or 0
if next_byte >= 0x80 and next_byte < 0xC0 then
code, size = (code - mask - 2) * 64 + next_byte, size + 1
else
code, size = utf8str:byte(pos), 1
end
mask = mask * 32
until code < mask
end
-- returns code, number of bytes in this utf8 char
return code, size
end
local map_1256_to_unicode = {
[0x80] = 0x20AC,
[0x81] = 0x067E,
[0x82] = 0x201A,
[0x83] = 0x0192,
[0x84] = 0x201E,
[0x85] = 0x2026,
[0x86] = 0x2020,
[0x87] = 0x2021,
[0x88] = 0x02C6,
[0x89] = 0x2030,
[0x8A] = 0x0679,
[0x8B] = 0x2039,
[0x8C] = 0x0152,
[0x8D] = 0x0686,
[0x8E] = 0x0698,
[0x8F] = 0x0688,
[0x90] = 0x06AF,
[0x91] = 0x2018,
[0x92] = 0x2019,
[0x93] = 0x201C,
[0x94] = 0x201D,
[0x95] = 0x2022,
[0x96] = 0x2013,
[0x97] = 0x2014,
[0x98] = 0x06A9,
[0x99] = 0x2122,
[0x9A] = 0x0691,
[0x9B] = 0x203A,
[0x9C] = 0x0153,
[0x9D] = 0x200C,
[0x9E] = 0x200D,
[0x9F] = 0x06BA,
[0xA0] = 0x00A0,
[0xA1] = 0x060C,
[0xA2] = 0x00A2,
[0xA3] = 0x00A3,
[0xA4] = 0x00A4,
[0xA5] = 0x00A5,
[0xA6] = 0x00A6,
[0xA7] = 0x00A7,
[0xA8] = 0x00A8,
[0xA9] = 0x00A9,
[0xAA] = 0x06BE,
[0xAB] = 0x00AB,
[0xAC] = 0x00AC,
[0xAD] = 0x00AD,
[0xAE] = 0x00AE,
[0xAF] = 0x00AF,
[0xB0] = 0x00B0,
[0xB1] = 0x00B1,
[0xB2] = 0x00B2,
[0xB3] = 0x00B3,
[0xB4] = 0x00B4,
[0xB5] = 0x00B5,
[0xB6] = 0x00B6,
[0xB7] = 0x00B7,
[0xB8] = 0x00B8,
[0xB9] = 0x00B9,
[0xBA] = 0x061B,
[0xBB] = 0x00BB,
[0xBC] = 0x00BC,
[0xBD] = 0x00BD,
[0xBE] = 0x00BE,
[0xBF] = 0x061F,
[0xC0] = 0x06C1,
[0xC1] = 0x0621,
[0xC2] = 0x0622,
[0xC3] = 0x0623,
[0xC4] = 0x0624,
[0xC5] = 0x0625,
[0xC6] = 0x0626,
[0xC7] = 0x0627,
[0xC8] = 0x0628,
[0xC9] = 0x0629,
[0xCA] = 0x062A,
[0xCB] = 0x062B,
[0xCC] = 0x062C,
[0xCD] = 0x062D,
[0xCE] = 0x062E,
[0xCF] = 0x062F,
[0xD0] = 0x0630,
[0xD1] = 0x0631,
[0xD2] = 0x0632,
[0xD3] = 0x0633,
[0xD4] = 0x0634,
[0xD5] = 0x0635,
[0xD6] = 0x0636,
[0xD7] = 0x00D7,
[0xD8] = 0x0637,
[0xD9] = 0x0638,
[0xDA] = 0x0639,
[0xDB] = 0x063A,
[0xDC] = 0x0640,
[0xDD] = 0x0641,
[0xDE] = 0x0642,
[0xDF] = 0x0643,
[0xE0] = 0x00E0,
[0xE1] = 0x0644,
[0xE2] = 0x00E2,
[0xE3] = 0x0645,
[0xE4] = 0x0646,
[0xE5] = 0x0647,
[0xE6] = 0x0648,
[0xE7] = 0x00E7,
[0xE8] = 0x00E8,
[0xE9] = 0x00E9,
[0xEA] = 0x00EA,
[0xEB] = 0x00EB,
[0xEC] = 0x0649,
[0xED] = 0x064A,
[0xEE] = 0x00EE,
[0xEF] = 0x00EF,
[0xF0] = 0x064B,
[0xF1] = 0x064C,
[0xF2] = 0x064D,
[0xF3] = 0x064E,
[0xF4] = 0x00F4,
[0xF5] = 0x064F,
[0xF6] = 0x0650,
[0xF7] = 0x00F7,
[0xF8] = 0x0651,
[0xF9] = 0x00F9,
[0xFA] = 0x0652,
[0xFB] = 0x00FB,
[0xFC] = 0x00FC,
[0xFD] = 0x200E,
[0xFE] = 0x200F,
[0xFF] = 0x06D2,
}
local map_unicode_to_1256 = {}
for code1256, code in pairs(map_1256_to_unicode) do
map_unicode_to_1256[code] = code1256
end
function string.fromutf8(utf8str)
local pos, result_1256 = 1, {}
while pos <= #utf8str do
local code, size = utf8_to_unicode(utf8str, pos)
pos = pos + size
code = code < 128 and code or map_unicode_to_1256[code] or ('?'):byte()
table_insert(result_1256, char(code))
end
return table_concat(result_1256)
end
function string.toutf8(str1256)
local result_utf8 = {}
for pos = 1, #str1256 do
local code = str1256:byte(pos)
table_insert(result_utf8, unicode_to_utf8(map_1256_to_unicode[code] or code))
end
return table_concat(result_utf8)
end
Usage is:
str:fromutf8() -- to convert from UTF-8 to cp1256
str:toutf8() -- to convert from cp1256 to UTF-8
Example:
-- This is cp1256 string
local str1256 = "1\128" -- "one euro" in cp1256
-- Convert it to UTF-8
local str_utf8 = str1256:toutf8() -- "1\226\130\172" -- one euro in utf-8
-- Convert it back from UTF-8 to cp1256
local str1256_2 = str_utf8:fromutf8()
So what I am trying to do here is for a given json_body which is decoded json into a table using cjson I want to remove a given element by a configurable value conf.remove.json, I feel I am pretty close but its still not working, and is there a better way? Is there a safe way to find the tables "depth" and then reach out like conf.remove.json= I.want.to.remove.this creates the behavior json_table[I][want][to][remove][this] = nil without throwing some kind of NPE?
local configRemovePath= {}
local configRemoveDepth= 0
local recursiveCounter = 1
local function splitString(inputstr)
sep = "%." --Split on .
configRemovePath={}
configRemoveDepth=0
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
configRemovePath[configRemoveDepth + 1] = str
configRemoveDepth = configRemoveDepth + 1
end
end
local function recursiveSearchAndNullify(jsonTable)
for key, value in pairs(jsonTable) do --unordered search
-- First iteration
--Sample Json below, where conf.remove.json = data.id and nothing happened. conf.remove.json=data.id
--{
--"data": {
-- "d": 2,
-- "id": 1
--}
--}
-- value = {"d": 2, "id": 1}, key = "data", configRemovePath[recursiveCounter] = "data" , configRemovePath ['data','id'] , configRemoveDepth = 2
if(type(value) == "table" and value == configRemovePath[recursiveCounter] and recursiveCounter < configRemoveDepth) then --If the type is table, the current table is one we need to dive into, and we have not exceeded the configurations remove depth level
recursiveCounter = recursiveCounter + 1
jsonTable = recursiveSearchAndNullify(value)
else
if(key == configRemovePath[recursiveCounter] and recursiveCounter == configRemoveDepth) then --We are at the depth to remove and the key matches then we delete.
for key in pairs (jsonTable) do --Remove all occurances of said element
jsonTable[key] = nil
end
end
end
end
return jsonTable
end
for _, name in iter(conf.remove.json) do
splitString(name)
if(configRemoveDepth == 0) then
for name in pairs (json_body) do
json_body[name] = nil
end
else
recursiveCounter = 1 --Reset to 1 for each for call
json_body = recursiveSearchAndNullify(json_body)
end
end
Thanks to any who assist, this is my first day with Lua so I am pretty newb.
This is the official answer, found a better way with the help of Christian Sciberras!
local json_body_test_one = {data = { id = {"a", "b"},d = "2" }} --decoded json w cjson
local json_body_test_two = {data = { { id = "a", d = "1" }, { id = "b", d = "2" } } }
local config_json_remove = "data.id"
local function dump(o) --Method to print test tables for debugging
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
local function splitstring(inputstr, sep)
if sep == nil then
sep = "%." --Dot notation default
end
local t={} ; i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
local function setjsonprop(json_object, path, newvalue)
local configarray = splitstring(path)
while (#configarray > 1) do
json_object = json_object[table.remove(configarray, 1)]
if(type(json_object) == "table" and #json_object > 0) then
local recursepath = table.concat(configarray, ".")
for _, item in pairs(json_object) do
setjsonprop(item, recursepath, newvalue)
end
return
end
end
json_object[table.remove(configarray, 1)] = newvalue
end
setjsonprop(json_body_test_one, config_json_remove, nil)
print(dump(json_body_test_one))
I need to convert a Json String to a table data structure in Lua. I am using the following code.
local json = require "json"
local t = {
["name1"] = "value1",
["name2"] = { 1, false, true, 23.54, "a \021 string" },
name3 = json.null
}
local encode = json.encode (t)
print (encode) --> {"name1":"value1","name3":null,"name2":[1,false,true,23.54,"a \u0015 string"]}
local decode = json.decode( encode )
But when I run the script, I get the following errors,
no field package.preload['json']
no file '/usr/local/share/lua/5.2/json.lua'
no file '/usr/local/share/lua/5.2/json/init.lua'
no file '/usr/local/lib/lua/5.2/json.lua'
no file '/usr/local/lib/lua/5.2/json/init.lua'
no file './json.lua'
no file '/usr/local/lib/lua/5.2/json.so'
no file '/usr/local/lib/lua/5.2/loadall.so'
no file './json.so'
So how to convert my json string to lua table?
maybe lua-cjsonis your friend:
install e.g. through luarocks:
$sudo luarocks install lua-cjson
then in lua:
local json = require('cjson')
local tab = json.decode(json_string)
json_string = json.encode(tab)
https://gist.github.com/tylerneylon/59f4bcf316be525b30ab
I found pure lua script file to parse json data (just one file).
local json = {}
-- Internal functions.
local function kind_of(obj)
if type(obj) ~= 'table' then return type(obj) end
local i = 1
for _ in pairs(obj) do
if obj[i] ~= nil then i = i + 1 else return 'table' end
end
if i == 1 then return 'table' else return 'array' end
end
local function escape_str(s)
local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'}
local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'}
for i, c in ipairs(in_char) do
s = s:gsub(c, '\\' .. out_char[i])
end
return s
end
-- Returns pos, did_find; there are two cases:
-- 1. Delimiter found: pos = pos after leading space + delim; did_find = true.
-- 2. Delimiter not found: pos = pos after leading space; did_find = false.
-- This throws an error if err_if_missing is true and the delim is not found.
local function skip_delim(str, pos, delim, err_if_missing)
pos = pos + #str:match('^%s*', pos)
if str:sub(pos, pos) ~= delim then
if err_if_missing then
error('Expected ' .. delim .. ' near position ' .. pos)
end
return pos, false
end
return pos + 1, true
end
-- Expects the given pos to be the first character after the opening quote.
-- Returns val, pos; the returned pos is after the closing quote character.
local function parse_str_val(str, pos, val)
val = val or ''
local early_end_error = 'End of input found while parsing string.'
if pos > #str then error(early_end_error) end
local c = str:sub(pos, pos)
if c == '"' then return val, pos + 1 end
if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end
-- We must have a \ character.
local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
local nextc = str:sub(pos + 1, pos + 1)
if not nextc then error(early_end_error) end
return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc))
end
-- Returns val, pos; the returned pos is after the number's final character.
local function parse_num_val(str, pos)
local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
local val = tonumber(num_str)
if not val then error('Error parsing number at position ' .. pos .. '.') end
return val, pos + #num_str
end
-- Public values and functions.
function json.stringify(obj, as_key)
local s = {} -- We'll build the string as an array of strings to be concatenated.
local kind = kind_of(obj) -- This is 'array' if it's an array or type(obj) otherwise.
if kind == 'array' then
if as_key then error('Can\'t encode array as key.') end
s[#s + 1] = '['
for i, val in ipairs(obj) do
if i > 1 then s[#s + 1] = ', ' end
s[#s + 1] = json.stringify(val)
end
s[#s + 1] = ']'
elseif kind == 'table' then
if as_key then error('Can\'t encode table as key.') end
s[#s + 1] = '{'
for k, v in pairs(obj) do
if #s > 1 then s[#s + 1] = ', ' end
s[#s + 1] = json.stringify(k, true)
s[#s + 1] = ':'
s[#s + 1] = json.stringify(v)
end
s[#s + 1] = '}'
elseif kind == 'string' then
return '"' .. escape_str(obj) .. '"'
elseif kind == 'number' then
if as_key then return '"' .. tostring(obj) .. '"' end
return tostring(obj)
elseif kind == 'boolean' then
return tostring(obj)
elseif kind == 'nil' then
return 'null'
else
error('Unjsonifiable type: ' .. kind .. '.')
end
return table.concat(s)
end
json.null = {} -- This is a one-off table to represent the null value.
function json.parse(str, pos, end_delim)
pos = pos or 1
if pos > #str then error('Reached unexpected end of input.') end
local pos = pos + #str:match('^%s*', pos) -- Skip whitespace.
local first = str:sub(pos, pos)
if first == '{' then -- Parse an object.
local obj, key, delim_found = {}, true, true
pos = pos + 1
while true do
key, pos = json.parse(str, pos, '}')
if key == nil then return obj, pos end
if not delim_found then error('Comma missing between object items.') end
pos = skip_delim(str, pos, ':', true) -- true -> error if missing.
obj[key], pos = json.parse(str, pos)
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '[' then -- Parse an array.
local arr, val, delim_found = {}, true, true
pos = pos + 1
while true do
val, pos = json.parse(str, pos, ']')
if val == nil then return arr, pos end
if not delim_found then error('Comma missing between array items.') end
arr[#arr + 1] = val
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '"' then -- Parse a string.
return parse_str_val(str, pos + 1)
elseif first == '-' or first:match('%d') then -- Parse a number.
return parse_num_val(str, pos)
elseif first == end_delim then -- End of an object or array.
return nil, pos + 1
else -- Parse true, false, or null.
local literals = {['true'] = true, ['false'] = false, ['null'] = json.null}
for lit_str, lit_val in pairs(literals) do
local lit_end = pos + #lit_str - 1
if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
end
local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
error('Invalid json syntax starting at ' .. pos_info_str)
end
end
return json
You can use json-lua. A pure lua implementation of json. First install json-lua using Luarocks. luarocks install json-lua . Then Use this code :
local json = require "json"
local t = {
["name1"] = "value1",
["name2"] = { 1, false, true, 23.54, "a \021 string" },
name3 = json.null
}
local encode = json:encode (t)
print (encode) --> {"name1":"value1","name3":null,"name2":[1,false,true,23.54,"a \u0015 string"]}
local decode = json:decode( encode )
Tested & Verified on win 7 64 bit with lua 5.1. lua-cjson is fine, but it is not a pure lua rock. So, it's installation will not be easier to you.