@@ -40,104 +40,103 @@ Primes are:
40
40
4 7
41
41
```
42
42
43
- ### Traversing (decoding JSON without creating the result on Lua side)
43
+ ### Traversing (dry run decoding JSON without creating the result on Lua side) and partial decoding of JSON
44
44
45
- Function ` json.traverse(s, callback, pos) ` traverses JSON and invokes user-supplied callback function
45
+ Traverse is useful to reduce memory usage: no memory-consuming objects are being created in Lua while traversing.
46
+ Function ` json.traverse(s, callback, pos) ` traverses JSON,
47
+ and invokes user-supplied callback function for each item found inside JSON.
48
+
49
+ Callback function arguments:
46
50
```
47
- callback function arguments: ( path, json_type, value, pos)
51
+ path, json_type, value, pos, pos_last
48
52
path is array of nested JSON identifiers, this array is empty for root JSON element
49
53
json_type is one of "null"/"boolean"/"number"/"string"/"array"/"object"
50
- value is defined when json_type is "boolean"/"number"/"string", otherwise value == nil
51
- pos is 1-based index of first character of current JSON element inside JSON string
52
- ```
53
- ``` lua
54
- json .traverse ([[ 42 ]] , callback )
55
- -- will invoke callback function 1 time:
56
- callback ( {}, " number" , 42 , 2 )
54
+ value is defined when json_type is "null"/"boolean"/"number"/"string", value == nil for "object"/"array"
55
+ pos is 1-based index of first character of current JSON element
56
+ pos_last is 1-based index of last character of current JSON element (defined only when "value" ~= nil)
57
57
```
58
- ``` lua
59
- json .traverse ([[ {"a":true, "b":null, "c":["one","two"], "d":{ "e":{}, "f":[] } } ]] , callback )
60
- -- will invoke callback function 9 times:
61
- callback ( {}, " object" , nil , 2 )
62
- callback ( {" a" }, " boolean" , true , 7 )
63
- callback ( {" b" }, " null" , nil , 17 )
64
- callback ( {" c" }, " array" , nil , 27 )
65
- callback ( {" c" , 1 }, " string" , " one" , 28 )
66
- callback ( {" c" , 2 }, " string" , " two" , 34 )
67
- callback ( {" d" }, " object" , nil , 46 )
68
- callback ( {" d" , " e" }, " object" , nil , 52 )
69
- callback ( {" d" , " f" }, " array" , nil , 60 )
70
- ```
71
-
58
+ By default, ` value ` is ` nil ` for JSON arrays/objects.
59
+ Nevertheless, you can get any array/object decoded (instead of get traversed) while traversing JSON.
60
+ In order to do that, callback function must return true when invoked for that element (when ` value == nil ` ).
61
+ This array/object decoded as Lua value will be sent to you on next invocation of callback function (` value ~= nil ` ).
72
62
73
- ### Reading JSON chunk-by-chunk
63
+ Traverse example:
74
64
75
- Both decoder functions ` json.decode() ` and ` json.traverse() ` can accept JSON as a "loader function" instead of a string.
76
- This function will be called repeatedly to return next parts (substrings) of JSON string.
77
- An empty string, nil, or no value returned from "loader function" means the end of JSON string.
78
- This may be useful for low-memory devices or for traversing huge JSON files.
65
+ ``` lua
66
+ json .traverse ([[ {"a":true, "b":null, "c":["one","two"], "d":{ "e":{}, "f":[] }, "g":["ten",20,-33.3] } ]] , callback )
67
+ -- will invoke callback function 13 times (if callback returns true for array "c" and object "e"):
68
+ -- path json_type value pos pos_last
69
+ -- ---------- --------- -------------- --- --------
70
+ callback ( {}, " object" , nil , 2 , nil )
71
+ callback ( {" a" }, " boolean" , true , 7 , 10 )
72
+ callback ( {" b" }, " null" , json .null , 17 , 20 ) -- special Lua value for JSON null
73
+ callback ( {" c" }, " array" , nil , 27 , nil ) -- this callback returned true (user wants to decode this array)
74
+ callback ( {" c" }, " array" , {" one" , " two" }, 27 , 39 ) -- the next invocation brings the result of decoding (value ~= nil)
75
+ callback ( {" d" }, " object" , nil , 46 , nil )
76
+ callback ( {" d" , " e" }, " object" , nil , 52 , nil ) -- this callback returned true (user wants to decode this object)
77
+ callback ( {" d" , " e" }, " object" , json .empty , 52 , 53 ) -- the next invocation brings the result of decoding (special Lua value for empty JSON object)
78
+ callback ( {" d" , " f" }, " array" , nil , 60 , nil )
79
+ callback ( {" g" }, " array" , nil , 70 , nil )
80
+ callback ( {" g" , 1 }, " string" , " ten" , 71 , 75 )
81
+ callback ( {" g" , 2 }, " number" , 20 , 77 , 78 )
82
+ callback ( {" g" , 3 }, " number" , - 33.3 , 80 , 84 )
83
+ ```
79
84
85
+ Example of callback function to get ` c ` and ` e ` elements been decoded (instead of traversed):
80
86
81
- ### Partial decoding of arbitrary element inside JSON
87
+ ``` lua
88
+ local result_c , result_e -- these variables will hold Lua objects for JSON elements "c" and "e"
89
+
90
+ local function callback (path , json_type , value , pos , pos_last )
91
+ -- print(table.concat(path, '/'), json_type, value, pos, pos_last)
92
+ local elem_name = path [# path ] -- last identifier in element's path
93
+ if elem_name == " c" then
94
+ result_c = value
95
+ elseif elem_name == " e" then
96
+ result_e = value
97
+ end
98
+ return elem_name == " c" or elem_name == " e" -- we want elements "c" and "e" to be decoded instead of be traversed
99
+ end
82
100
83
- Instead of decoding whole JSON, you can decode its arbitrary element (e.g, array or object) by specifying the position where this element starts.
84
- In order to do that, at first you have to traverse JSON to get all positions you need.
101
+ json . traverse ( JSON_string , callback )
102
+ ```
85
103
104
+ ### Reading JSON from file without preloading whole JSON as (huge) Lua string
86
105
87
- ### Partial decoding of JSON with reading JSON from file
106
+ Both functions ` json.decode() ` and ` json.traverse() ` can accept JSON as a "loader function" instead of a "whole JSON string".
107
+ This function will be called repeatedly to return next parts (substrings) of JSON.
108
+ An empty string, nil, or no value returned from "loader function" means the end of JSON.
109
+ This may be useful for low-memory devices or for traversing huge JSON files.
88
110
89
111
``` lua
90
- -- This is content of "data.txt" file:
91
- -- {"aa":["qq",{"k1":23,"gg":"YAY","Fermat_primes":[3, 5, 17, 257, 65537],"bb":0}]}
92
-
93
- -- We want to extract (as Lua array) only "Fermat_primes" from this JSON
94
- -- without loading whole JSON to Lua.
95
-
96
- local json = require (' json' )
97
-
98
112
-- Open file
99
- local file = io.open (' data .txt' , ' r' )
113
+ local file = assert ( io.open (' large_json .txt' , ' r' ) )
100
114
101
- -- Define loader function which will read the file in 64-byte chunks
115
+ -- Define loader function for reading the file in 4KByte chunks
102
116
local function my_json_loader ()
103
- return file :read (64 )
117
+ return file :read (4 * 1024 ) -- 64 Byte chunks are recommended for RAM-restricted devices
104
118
end
105
119
106
- local position -- We need to know the position of Fermat primes array inside this JSON
107
-
108
- -- Prepare callback function for traverse
109
- local function my_callback (path , json_type , value , pos )
110
- if # path == 3
111
- and path [1 ] == " aa"
112
- and path [2 ] == 2
113
- and path [3 ] == " Fermat_primes"
114
- and json_type == " array"
115
- then
116
- position = pos
120
+ if you_want_to_traverse_JSON_or_to_decode_file_partially then
121
+
122
+ -- Prepare callback function
123
+ local function my_callback (path , json_type , value , pos , last_pos )
124
+ -- Do whatever you need here
125
+ -- (see examples on using json.traverse)
117
126
end
118
- end
119
127
120
- -- Step #1: Traverse to get the position
121
- file : seek ( " set " , 0 )
122
- json .traverse (my_json_loader , my_callback )
128
+ -- Do traverse
129
+ -- Set initial position as 3-rd argument (default 1) if JSON is stored not from the beginning of your file
130
+ json .traverse (my_json_loader , my_callback )
123
131
124
- -- Step #2: Decode only Fermat_primes array
125
- file :seek (" set" , position - 1 )
126
- local FP = json .decode (my_json_loader )
132
+ elseif you_want_to_decode_the_whole_file then
127
133
128
- print (' Fermat_primes:' ); for k , v in ipairs (FP ) do print (k , v ) end
134
+ -- Do decode
135
+ -- Set initial position as 2-rd argument (default 1) if JSON is stored not from the beginning of your file
136
+ result = json .decode (my_json_loader )
129
137
138
+ end
139
+
130
140
-- Close file
131
141
file :close ()
132
142
```
133
-
134
- Output:
135
-
136
- ```
137
- Fermat_primes:
138
- 1 3
139
- 2 5
140
- 3 17
141
- 4 257
142
- 5 65537
143
- ```
0 commit comments