JSON structs with abstracts issue

91 views
Skip to first unread message

Roel Hacking

unread,
Sep 23, 2017, 6:48:57 AM9/23/17
to Haxe
I'm trying to write a Tiled JSON importer, however I ran into an issue. I have the following structs: 

typedef Tiled = {
var width:Int;
var height:Int;
var tilesets:Array<TileSet>;
var layers:Array<Layer>;
}
...
typedef Layer = {
var data:EncodedString;
}

EncodedString is an abstract defined like this: 
abstract EncodedString(Array<Int>) to Array<Int> {
function new(data : Array<Int>) {
this = data;
}
@:from
static function decode(str : String) {
var compressed = haxe.crypto.Base64.decode(str);
var raw = haxe.zip.Uncompress.run(compressed);
var data:Array<Int> = [];
for (i in 0...Std.int(raw.length/4)) {
var value = raw.getInt32(i*4);
data.push(value);
}
return new EncodedString(data);
}
}

I would expect this to cast the string in the JSON file to an EncodedString, such that I could do the following:
var data:Array<Int> = tiled.TiledJSON.parseFile("path").layers[0].data; // parseFile simply uses haxe.Json.parse
trace(data);

However, this results in the original string in the file being traced, rather than an integer array. Trace data[0] results in "Invalid array access". This is running in the Haxe 3.4.3 interpreter. Can I not expect this to work in the interpreter (I have yet to test other targets)?

Ben Merckx

unread,
Sep 23, 2017, 10:57:18 AM9/23/17
to Haxe
You'd need to do this the other way around. The data is in string format so what you want is an abstract EncodedString(String) with a @:to method to create the Array<Int>.

Roel Hacking

unread,
Sep 23, 2017, 1:27:23 PM9/23/17
to Haxe
That seems to work, though I would kinda expect the other approach also to work (or at least throw an error). 

Op zaterdag 23 september 2017 16:57:18 UTC+2 schreef Ben Merckx:

Ben Merckx

unread,
Sep 23, 2017, 4:30:07 PM9/23/17
to haxe...@googlegroups.com
Abstracts are a compile time feature. Parsing json results in a Dynamic which you can cast to any other type. The compiler can't check if that's correct. So something like this will compile but will also obviously create runtime errors:
var arr: Array<Int> = Json.parse("somestring");
Casting it to an EncodedString(Array<Int>) will result in the same problems.
You could also first correctly type it as String and then cast it to the EncodedString which will trigger the @:from method.

Roel Hacking

unread,
Sep 24, 2017, 12:47:25 PM9/24/17
to Haxe
I suppose that makes sense, though I would expect a runtime error to be thrown when the cast is made, not when I try to use it later. It seems that even though the variable has type Array<Int> and is not null, I still can't expect there to be an array. Anyway, thanks for your help!

Op zaterdag 23 september 2017 22:30:07 UTC+2 schreef Ben Merckx:

Juraj Kirchheim

unread,
Sep 25, 2017, 2:59:51 AM9/25/17
to haxe...@googlegroups.com
That's because anonymous types simply are `Dynamic` at runtime and haxe.Json.parse returns `Dynamic` too, so no runtime type checking occurs.

You could use tink_json, which generates type safe parsers: https://github.com/haxetink/tink_json

You can control parsing by specifying alternative representation (https://github.com/haxetink/tink_json#custom-abstracts) or custom parsers (not documented, but there's an example in the tests: https://github.com/haxetink/tink_json/blob/2d0e6f141bad4d4d8287ddb40fc40fa5a000863a/tests/Types.hx#L46)

Best,
Juraj

--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages