📜 ⬆️ ⬇️

Corona SDK json library anomaly

Hello. Few people will have a secret that using json in your projects on the Corona SDK can make some things quite comfortable. I discovered one interesting anomaly when working with the library, which, as practice has shown, is not at all a mistake, but rather a feature about which you should know and be ready for it. Consider all the details.

1. Description of the problem


Suppose we have a Lua array of the following form:

ar = {23,45,56,'weer'} 

If everything is normal with your understanding of the language, this array appears to you as shown below, simply degreased due to the circumstances - it is possible:
')
 ar = {[1] = 23,[2] = 45,[3] = 56,[4] = 'weer'} 

If you decide to convert this array to json, then at the output you will get something like this (of course, we will not forget to connect the json library):

 ar = {[1] = 23,[2] = 45,[3] = 56,[4] = 'weer'} json = require "json" local str = json.encode(ar) print(str)--> [23,45,56,"weer"] 

As you can see, json also knows about the existence of this kind of simplified notation in which if the elements of the array go sequentially, they will not be explicitly numbered, which is great since json is not uncommonly used in our projects for the network protocol or saving to the file and the extra size is completely for what.

Now we will try to convert the drain back to Lua table and display its contents while paying attention to the data types of the keys:

 local str = '[23,45,56,"weer"]' local ar2 = json.decode(str) for k,v in pairs(ar2)do print(k,type(k),v) end --[[ 1 number 23 2 number 45 3 number 56 4 number weer ]] 

As you can see, the result was more than expected, the table ar at the input fully corresponds to the table ar2 at the output. Now, following the previous 2 points, but the input will be an array in which there is an interrupted sequence in the array, let's say we add key 6 (bypassing 5) with the value 'wtf'. We are criminal.

 json = require "json" ar = {23,45,56,'weer',[6] = 'wtf'} local str = json.encode(ar) print(str)-->[23,45,56,"weer",null,"wtf"] local t = json.decode(str) for k,v in pairs(t)do print(k,type(k),v) end --[[ 1 number 23 2 number 45 3 number 56 4 number weer 6 number wtf ]] 

As you can see, everything turned out remarkably well as json.encode expected this trick and instead of the nonexistent 5 key inserted null and so everything ended in success, we will not stop and add another 777 key to the array with a value of 1, most likely we expect that the table is converted in the same way and will include 770 null - this is certainly not the best option since it will take much more space, but there is no other way in json that would create a complete analog of this lua table since in json the key is not maybe clearly as a number. We look what happened.

 json = require "json" ar = {23,45,56,'weer',[6] = 'wtf',[777] = 1} local str = json.encode(ar) print(str)-->{"1":23,"2":45,"3":56,"4":"weer","6":"wtf","777":1} local t = json.decode(str) for k,v in pairs(t)do print(k,type(k),v) end --[[ 1 string 23 777 string 1 3 string 56 2 string 45 4 string weer 6 string wtf ]] 

As you can see, the encoder went the other way and converted all the keys with the value type number to string. It’s no secret that such a feature can and will lead to errors (if you don’t know about it), or to crutches with the use of explicit tonumber / tostring transformations when working with this table, in any case it will not bring much pleasure. Consider how to deal with this.

2. Problem solving


In order to solve this problem, you can write a function that will convert all the arrays in which the problem arose to the initial state, then you will have to constantly remember this problem in the code and apply this function everywhere, this is a normal way, but not the best, and therefore there is a plan B We will override the implementation of the json.decode function so that all problem areas are automatically converted and this solution works both on the first nesting in the transferred array and on deeper investments. We will do the redefinition of the function's value immediately after we connect the json library to the project. There is the following implementation:

 json = require "json" local jd = json.decode--   local norm_ar json.decode = function(ar,not_convert)--  local res = jd(ar) --     --    norm_ar = function(root) local res = {} --  for k,v in pairs(root)do res[k] = v end --   for k,v in pairs(res) do if type(k) == 'string' and--  string.match(tostring(k),'%D') == nil then--    -- string -> number root[tonumber(k)] = root[k] root[k] = nil--   end --      if type(v) == 'table' then norm_ar(v) end end return root end --   not_convert    return not_convert and res or norm_ar(res) end 

Now let's try to perform the previous operation again:

 ar = {23,45,56,'weer',[6] = 'wtf',[777] = 1} local str = json.encode(ar) print(str)-->{"1":23,"2":45,"3":56,"4":"weer","6":"wtf","777":1} local t = json.decode(str) for k,v in pairs(t)do print(k,type(k),v) end --[[ 1 number 23 2 number 45 3 number 56 4 number weer 6 number wtf 777 number 1 --]] 

As you can see, everything worked and the array retained its former structure. Lastly, I want to add that if for some reason you need to avoid this conversion, a new implementation option json.decode supports the second optional parameter that disables the conversion.

Bye everyone!

Source: https://habr.com/ru/post/422591/


All Articles