为了更好地了解一切是如何工作的,我使用了一些更先进的东西,例如haxe的摘要,构建了我自己的Yaml Parser,并且我已经打了另一面墙。
重要的部分是一个抽象的abstract YamlMap (StringMap<Either<String, YamlMap>>)
,它充当我数据的容器。 “节点”可以是字符串,也可以是另一个yamlmap,更深入到树中。
此YamlMap
有多种获取数据的方法,如getMap(key):YamlMap
和getString(key):String
,以及动态访问方法getDynamic(key):Dynamic
。
不幸的是,似乎只有一个@:arrayAccess
可以是抽象的,或者我错过了一些东西。你似乎也不能使用字符串“arrayAccess”一个动态对象,或者至少编译器阻止我这样做。
所以,这有效:data.getMap('test_node').getMap('sub_node1').getString('value2')
但这不是:data['test_node']['sub_node2']['value2']
如果我将getDynamic
设置为arrayAccess,它会告诉我sub_node2
应该是一个int。但是,如果我将getMap
和getString
都设置为arrayAccess,它将始终调用第一个arrayAccess标记的方法。因此,当尝试获取“值”(这是一个字符串,但代码试图获取一个映射)或者没有编译时它要么失败,因为,我猜,它试图从字符串而不是映射中访问字符位置。
所以,我的猜测是,与此manual entry相关,任何非抽象的arrayAccess都被锁定为int,因此动态对象拒绝使用字符串访问。
我能想到的一个可能的解决方案是,而不是使用动态值来返回某种抽象,这种抽象会在投射时“扁平”为正确的类型。是否还有其他方法可以在动态地图上实现字符串化数组访问?
注意:在某种程度上,这是出于好奇,因为对不同的地图和字符串调用的当前方法适用于日常使用。我也知道现有的yaml haxelib,但这是一次学习经历,因为它试图取代有时会出错的haxelib。
这是YamlMap摘要的Pastebin,适合所有感兴趣的人。
答案 0 :(得分:2)
首先,摘要可以有多个@:arrayAccess
方法:
abstract MultiArrayAccess({}) from {} {
public function new() this = {};
@:arrayAccess function getInt(i:Int) return i;
@:arrayAccess function getString(s:String) return Std.parseInt(s);
}
var i = new MultiArrayAccess();
trace(i[1]); // 1
trace(i["3"]); // 3
这个例子的区别在于键类型不同。这是必要的,因为摘要上的数组访问(以及一般的摘要)是编译时功能。以下是这两个数组访问的AST转储:
_Main.MultiArrayAccess_Impl_.getInt(i, 1)
_Main.MultiArrayAccess_Impl_.getString(i, "3")
因此,编译器必须知道在编译时调用的方法。在您的情况下这是不可能的,因为两种方法的密钥类型都是String
。将getString()
和getMap()
替换为[]
,您的信息就会丢失。
似乎你不能使用字符串“arrayAccess”动态对象,或者至少编译器阻止我这样做。
这是正确的,Dynamic
上不允许进行数组访问。但是,标准库有一个名为haxe.DynamicAccess
的抽象,它是一个使用Reflect.field()
实现数组访问的抽象。当然,这会导致运行时开销。你可以拥有一个完全动态的实现:
typedef YamlMap = haxe.DynamicAccess<YamlMapValue>;
abstract YamlMapValue(YamlMap) from YamlMap {
@:arrayAccess function get(key:String):YamlMapValue {
return this.get(key);
}
@:arrayAccess function setMap(key:String, value:YamlMap) {
Reflect.setField(this, key, value);
}
@:arrayAccess function setString(key:String, value:String) {
Reflect.setField(this, key, value);
}
}
var data = new YamlMap();
data["test_node"] = new YamlMap();
data["test_node"]["sub_node1"] = new YamlMap();
data["test_node"]["sub_node1"]["value2"] = "foo";
var map:YamlMapValue = data["test_node"]["sub_node1"];
trace(map["value2"]); // foo
但是,这不一定是个好主意,因为你会以这种方式失去很多类型安全性(因此,性能)。