Difficulties to parse and use complexe Json

Hi,

I have a complexe Json that I managed to export by code, but now I have difficulties to import it elsewhere…
Here’s a short example of my Json:

{"sizes": [
	{"1x1":[
		{
			"main": "[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]",
			"door_up": "[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]"
		},
		{
			"main": "[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]",
			"door_up": "[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]"
		}]},
	{"1x2":[
		{
			"main": "[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]",
			"door_up": "[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]"
		}]}
]}

First thing I do to get the file is this:

levels_datas = Json.parse(File.getContent(path + file_name));

for (i in 0...levels_datas.sizes.length) {
	trace(levels_datas.sizes[i]);
}

The trace is working perfectly, so no problem here, but I can’t “use” my values…
My goal here is to be able to use the imported Json datas into something like this:

private var levels:Map<String, Array<Level_Datas>> = new Map();

The keys of the Map would be the different sizes: 1x1, 1x2, 1x3, ... and the value of those keys would be Array<Level_Datas> which is every level for each size. Right now, Level_Datas is a class that I use to manage every grid of a level that have public variables like these:

public var main:Array<Array<Int>> = new Array(); //The main grid
public var door_up:Array<Array<Int>> = new Array();

I suppose that I’ll need a typedef, but everything I tried didn’t work…

Thank you in advance for your help!

Can you tell how haxe.Json is parsing this? Does it handle it as an array, or as a String? If it is a String, you might be able to do a little bit of parsing yourself to split it into an array properly

To make your json working with fixed variables in typedef, you need to remove the " of your arrays and add an underscore before 1x1, etc. (you can’t have a variable beginning with a number).

//remove the "
"main": [[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],
//example of typedef
typedef Your_Level = {
    var _1x1:Level_Array;
    var _1x2:Level_Array;
}

But if you really need to use a map, you have more changes to make. Please see my examples below.

https://try.haxe.org/#814B6

So, your Json should looks like this:

{"sizes":[{"h":{"1x1":[{"main":[[0,0]],"door_up":[[0,0]]}],"1x2":[{"main":[[0,0]],"door_up":[[0,0]]}]}}]}

All typedef needed to have a Map in your Json:

typedef Game = {
	var sizes:Array<Map_Level>;
}
typedef Map_Level = Map<String, Level_Array>;
typedef Level_Array = Array<Level_Data>;
typedef Level_Data = {
   var main : Array<Array<Int>>;
   var door_up : Array<Array<Int>>;
}

It clarifies some stuff, thank you!

I understand why Singmajesty was wondering about the “” and I don’t know why I thought that I needed to put all my values inbetween “”. I think it was still working though, but that was not necessary.

I’ll be able to do more tests soon and it’ll probably finaly works! (I hope!)

Can you just tell me where the “h” is coming from?

Thanks!

look at my try.haxe code. It’s how the Map is stringified by the json class.

Use this code to generate your typedefs from your JSON string.

(I haven’t made my time to create a website to do it for you, sorry :stuck_out_tongue: )

1 Like

You shouldn’t rely on the underlying Map implementation (using the h field) and instead do:

typedef Map_Level = DynamicAccess<Level_Array>; // same Haxe API as Map

Documentation of DynamicAccess

Can I ask why? I don’t understand what DynamicAccess brings…

First of all, the trick of using {"h":...} and soft-casting as Map is strictly JS-specific, so it won’t work for other targets, Map underlying implementation could change, and I believe that some keys are escaped so you may have weird bugs.

haxe.DynamicAccess<T> is like a Map<String, T>; it follows the exact same API but is suitable for a map-like JSON object.

var m:DynamicAccess<Int> = {
    foo: 12
};
var foo = m.get('foo'); // compiles in JS as: var foo = m['foo'];
m.set('bar', 42); // compiles in JS as: m['bar'] = 42;
// now m is: { foo:12, bar: 42 }

Other Haxe targets will use Reflect to get/set values so your code will be compatible.

3 Likes

Thank you all for the help!

I finally got it to work using more than one typedef:

typedef Levels_File = {
	var sizes:Array<Map_Levels>;
}
//Level_Datas could have been a typedef, but it was already a Class with with public variables
typedef Map_Levels = DynamicAccess<Array<Level_Datas>>;

Then, here’s the import setup:

//Get Json
//levels_data:Levels_File
levels_datas = Json.parse(File.getContent(path + file_name));
			
//Get all levels and put them into their corresponding size in a Map<String, Array<Level_Datas>>
//levels_import:Map<String, Array<Level_Datas>>
for (i in 0...levels_datas.sizes.length) {
	var key:String = levels_datas.sizes[i].keys()[0];
	levels_import.set(key, levels_datas.sizes[i].get(key));
}

//Create a vignette for each level
for (size in levels_import.keys()) {
	var size_levels:Array<Level_Datas> = levels_import.get(size);
	
	for (i in 0...size_levels.length) {
		VignettesManager.add_vignette(size_levels[i]);
	}
}