It's possible read JSON files at compile time into strictly typed objects in Haxe.
Normally you might load a JSON file with something like this:
var json = haxe.Json.parse(sys.io.File.getContent(path));
Instead, if you load the JSON in a macro, then the JSON data will be available at compile time and therefore the types will be known:
Create a file called JsonMacro.hx (or whatever you like) and add this:
macro function load(path:String) { // Register a dependency to the external file so the Haxe compilation cache is invalidated if the file changes. haxe.macro.Context.registerModuleDependency(haxe.macro.Context.getLocalModule(), path); return try { var json = haxe.Json.parse(sys.io.File.getContent(path)); macro $v{json}; } catch (e) { haxe.macro.Context.error('Failed to load json: $e', haxe.macro.Context.currentPos()); } }
Then use this to load your JSON instead:
var leveldata = JsonMacro.load('leveldata.json'); for (i in leveldata.array) { // works now because we know the type of the leveldata object }
Explanation: We run the original Json.parse(File.getContent())
snippet in a macro function so it will execute when Haxe compiles our calls to JsonMacro.load()
. Instead of returning the JSON object, in macros we need to return syntax. So we must convert our JSON object into Haxe syntax – just as if we'd typed our JSON out manually as Haxe objects. Fortunately there's a built-in operator for converting values into Haxe syntax, it's the 'macro-reification-value-operator': $v{ some-basic-value }
. We could also use Context.makeExpr(value, position)
to do the same job. We wrap the JSON reading in a try-catch
so we can tidy-up error reporting a little.
With this approach, the JSON's content is embedded into your compiled program and not loaded at runtime, therefore, the path argument must be a constant string and cannot be an expression evaluated at runtime.