Reading JSON file from ResourceDirectory: no such file or directory

I’m trying to load a JSON file from my ResourcesDirectory on Android. I’m aware that there are some restrictions but my code is pretty straightforward.

[lua]

local task=getTask({
    path=“data”,
    baseDir=system.ResourceDirectory
  },“practice”)

function getTask(pathParam,folder)
  local path=toPath(pathParam)
  path=path…"/"…folder…"/task.json"

  local fh,err=io.open(path,“r”)
  if not err then
    local contents=fh:read("*a")
    fh:close()
    local task=json.decode(contents)
  end

  assert(not err,err)
  …

end

function toPath(param)
  local baseDir=param.baseDir
  assert((baseDir~=nil) ~= param.isAbsolute,“Path cannot be absolute and in system directory”)
  if not param.isAbsolute then
    return system.pathForFile(param.path,baseDir)
  end
  return param.path
end

[/lua]

Simply put it defines a table object that is converted in this case to a proper path. It appends a folder to the path and looks for a json file at that location. I’ve cut the code down for clarity (the actual codebase has to run on desktop as well) hence the support for absolute paths (the user can enter them manually in that environment).

When running on an Android device I get an error: /data/data/bundle/file/coronaResources/data/practice/task.json: No such file or directory. However if I unzip the apk I can see the JSON file at assets/data/practice/task.json! What am I doing wrong? The code runs fine on iOS and Windows desktop.

Wait, this isn’t to do with subdirectories is it? I just saw this post:

https://forums.coronalabs.com/topic/7722-subdirectories-in-systemresourcedirectory/#entry

Read the “Gotchas” for system.ResourceDirectory.  https://docs.coronalabs.com/api/library/system/ResourceDirectory.html

Rob

I’m confused, the gotchas say:

File access in Corona is based on the underlining operating system which varies by platform. On iOS devices, you can access files in all of the directories described above. On Android, however, there is no literal system.ResourceDirectory because all resource files reside inside a compressed .apk file.

Corona allows direct loading of images and audio files using the appropriate APIs, but it has limited access to resource files on Android using the file I/O APIs. Specifically, the following types can not be read from the resources directory: .html, .htm., .3gp, .m4v, .mp4, .png, .jpg, and .ttf.

Because of this limitation, if you have files of these types bundled in the core directory that you need to copy to another directory, you must change the file name so it can be accessed by the file I/O APIs. For example, if you want to move cat.png from the resource directory to the documents directory, it must be renamed cat.png.txt to be copied. See here for details.

 

There’s nothing there about JSON files, and nothing there about subdirectories. An apk is just a zip file, and zip files preserve directory structure don’t they?

Zip files supports subdirectories.

I’m not sure what’s going on. Clearly the path you’re printing out /data/data… isn’t seeing the “assets/” folder. I would print out the various path values and make sure that the final system.pathForFile() is the right path.

Rob

Okay, I think I’ve figured this out!

I wrote a bunch of tests in the form:

[lua]

function testLoad()

  local experimentdirectoryfinder=require “util.experimentdirectoryfinder”

  local path=toPath({

    path=“data”,

    baseDir=system.ResourceDirectory

  })

  assert(path,“main: toPath path returned nil”)

  path=path…"/path/to/json.json"

  assert(jsonreader.load(path),"main: No valid son file found in "…path)

  print (“Success”,path)

end

local ok,msg=pcall(testLoad)

if not ok then

  print(msg)

end

[/lua]

I had each of these tests try to load different critical JSON files. toPath is the function I have defined in my first post. jsonreader just contains the earlier JSON loading code in my first post (returning data,err).

I also have some code which directly checked system.pathForFile was returning a value for a list of paths. In my tests, I included this first and then went on to try to specifically load the JSON files. 

[lua]

local paths={

  “data/path/to/json.json”

}

function testPath()

  local pth=table.remove(paths)

  local p=system.pathForFile(pth,system.ResourceDirectory)

  assert(p,"main: no path is nil " … pth)

  print (“Success”,pth,p)

end

local pnum=#paths

for i=1,pnum do

  local ok,msg=pcall(testPath)

  if not ok then

    print (msg)

  end

end

[/lua]

Now! If I run the system.pathForFile tests, then the prior JSON file load will succeed! If comment out the system.pathForFile tests the JSON load will fail! 

I am assuming that Corona is looking for file paths in my code and making sure those resources are available. However this fails because the JSON file test used string concatenation to create the path. Does that sound about right?

Wait, this isn’t to do with subdirectories is it? I just saw this post:

https://forums.coronalabs.com/topic/7722-subdirectories-in-systemresourcedirectory/#entry

Read the “Gotchas” for system.ResourceDirectory.  https://docs.coronalabs.com/api/library/system/ResourceDirectory.html

Rob

I’m confused, the gotchas say:

File access in Corona is based on the underlining operating system which varies by platform. On iOS devices, you can access files in all of the directories described above. On Android, however, there is no literal system.ResourceDirectory because all resource files reside inside a compressed .apk file.

Corona allows direct loading of images and audio files using the appropriate APIs, but it has limited access to resource files on Android using the file I/O APIs. Specifically, the following types can not be read from the resources directory: .html, .htm., .3gp, .m4v, .mp4, .png, .jpg, and .ttf.

Because of this limitation, if you have files of these types bundled in the core directory that you need to copy to another directory, you must change the file name so it can be accessed by the file I/O APIs. For example, if you want to move cat.png from the resource directory to the documents directory, it must be renamed cat.png.txt to be copied. See here for details.

 

There’s nothing there about JSON files, and nothing there about subdirectories. An apk is just a zip file, and zip files preserve directory structure don’t they?

Zip files supports subdirectories.

I’m not sure what’s going on. Clearly the path you’re printing out /data/data… isn’t seeing the “assets/” folder. I would print out the various path values and make sure that the final system.pathForFile() is the right path.

Rob

Okay, I think I’ve figured this out!

I wrote a bunch of tests in the form:

[lua]

function testLoad()

  local experimentdirectoryfinder=require “util.experimentdirectoryfinder”

  local path=toPath({

    path=“data”,

    baseDir=system.ResourceDirectory

  })

  assert(path,“main: toPath path returned nil”)

  path=path…"/path/to/json.json"

  assert(jsonreader.load(path),"main: No valid son file found in "…path)

  print (“Success”,path)

end

local ok,msg=pcall(testLoad)

if not ok then

  print(msg)

end

[/lua]

I had each of these tests try to load different critical JSON files. toPath is the function I have defined in my first post. jsonreader just contains the earlier JSON loading code in my first post (returning data,err).

I also have some code which directly checked system.pathForFile was returning a value for a list of paths. In my tests, I included this first and then went on to try to specifically load the JSON files. 

[lua]

local paths={

  “data/path/to/json.json”

}

function testPath()

  local pth=table.remove(paths)

  local p=system.pathForFile(pth,system.ResourceDirectory)

  assert(p,"main: no path is nil " … pth)

  print (“Success”,pth,p)

end

local pnum=#paths

for i=1,pnum do

  local ok,msg=pcall(testPath)

  if not ok then

    print (msg)

  end

end

[/lua]

Now! If I run the system.pathForFile tests, then the prior JSON file load will succeed! If comment out the system.pathForFile tests the JSON load will fail! 

I am assuming that Corona is looking for file paths in my code and making sure those resources are available. However this fails because the JSON file test used string concatenation to create the path. Does that sound about right?