Groovy 中的动态直接字段访问?
Dynamic direct field access in Groovy?
XmlSlurper
的解析对象直接字段访问 (groovy.xml.slurpersupport.NodeChild.@someAttributeName
) 是如何工作的?考虑一个输入文件 foobar.xml
:
<root foo="bar">
<foobar>Hi</foobar>
</root>
和Groovy脚本:
import groovy.xml.XmlSlurper
def xml = new XmlSlurper().parse new File('./foobar.xml')
println xml.foobar
println xml.@foo
输出:
Hi
bar
据我了解,xml.foobar
(一个不存在的属性)可以通过使用元编程方法propertyMissing()
来处理(类似于methodMissing()
不存在的方法).但是,我似乎找不到像 foo
这样的直接访问字段的动态模拟。我怎么能实现类似的东西?即,我可以创建一个 class 动态处理 property/method 访问(例如,连同支持映射)和上面的元编程方法,但似乎没有等效的字段,例如:
class DynamicTest {
def propertyMissing(String propertyName) {
println "Hit missing property $propertyName"
}
def methodMissing(String methodName, def args) {
println "Hit missing method $methodName"
}
// nothing like this exists?
def fieldMissing(String fieldName) {
println 'Hit missing field $fieldName'
}
}
def obj = new DynamicTest()
obj.test1() // Hit missing method test1
obj.test2 // Hit missing property test2
obj.@test3 // Caught: groovy.lang.MissingFieldException: No such field: test3 for class: DynamicTest
请注意,我在 Groovy 和元编程方面有一天的经验,所以我不太确定我在这里使用的语言是否正确。我的理解是 xml.foobar
是一种 Groovy 类型的元编程 "field"(也可以使用 xml.getProperty('foobar')
、xml['foobar']
和 xml.getAt('foobar')
访问) ,而 xml.@foo
是一个普通的,类似于 Java 的字段。如果对上述问题有任何固有的误解,请告诉我。
所以,你可以去看看Node
over here
的源代码
神奇之处在于 lines 55-58 上的静态初始化程序块,它使用捕获属性获取和设置的新元类调用 setMetaclass
转换为 Groovy 你最终会得到这样的结果:
class Example {
static {
setMetaClass(GroovySystem.metaClassRegistry.getMetaClass(Example), Example)
}
def get(name) {
println "Getting $name"
}
def set(name, value) {
println "Setting $name to $value"
}
protected static void setMetaClass(final MetaClass metaClass, Class nodeClass) {
final MetaClass newMetaClass = new DelegatingMetaClass(metaClass) {
@Override
def getAttribute(object, String attribute) {
object.get("@$attribute")
}
@Override
void setAttribute(object, String attribute, newValue) {
object.set("@$attribute", newValue)
}
};
GroovySystem.metaClassRegistry.setMetaClass(nodeClass, newMetaClass);
}
}
def e = new Example()
e.@woo // prints "Getting @woo"
e.@woo = 'yay' // prints "Setting @woo to yay"
XmlSlurper
的解析对象直接字段访问 (groovy.xml.slurpersupport.NodeChild.@someAttributeName
) 是如何工作的?考虑一个输入文件 foobar.xml
:
<root foo="bar">
<foobar>Hi</foobar>
</root>
和Groovy脚本:
import groovy.xml.XmlSlurper
def xml = new XmlSlurper().parse new File('./foobar.xml')
println xml.foobar
println xml.@foo
输出:
Hi
bar
据我了解,xml.foobar
(一个不存在的属性)可以通过使用元编程方法propertyMissing()
来处理(类似于methodMissing()
不存在的方法).但是,我似乎找不到像 foo
这样的直接访问字段的动态模拟。我怎么能实现类似的东西?即,我可以创建一个 class 动态处理 property/method 访问(例如,连同支持映射)和上面的元编程方法,但似乎没有等效的字段,例如:
class DynamicTest {
def propertyMissing(String propertyName) {
println "Hit missing property $propertyName"
}
def methodMissing(String methodName, def args) {
println "Hit missing method $methodName"
}
// nothing like this exists?
def fieldMissing(String fieldName) {
println 'Hit missing field $fieldName'
}
}
def obj = new DynamicTest()
obj.test1() // Hit missing method test1
obj.test2 // Hit missing property test2
obj.@test3 // Caught: groovy.lang.MissingFieldException: No such field: test3 for class: DynamicTest
请注意,我在 Groovy 和元编程方面有一天的经验,所以我不太确定我在这里使用的语言是否正确。我的理解是 xml.foobar
是一种 Groovy 类型的元编程 "field"(也可以使用 xml.getProperty('foobar')
、xml['foobar']
和 xml.getAt('foobar')
访问) ,而 xml.@foo
是一个普通的,类似于 Java 的字段。如果对上述问题有任何固有的误解,请告诉我。
所以,你可以去看看Node
over here
神奇之处在于 lines 55-58 上的静态初始化程序块,它使用捕获属性获取和设置的新元类调用 setMetaclass
转换为 Groovy 你最终会得到这样的结果:
class Example {
static {
setMetaClass(GroovySystem.metaClassRegistry.getMetaClass(Example), Example)
}
def get(name) {
println "Getting $name"
}
def set(name, value) {
println "Setting $name to $value"
}
protected static void setMetaClass(final MetaClass metaClass, Class nodeClass) {
final MetaClass newMetaClass = new DelegatingMetaClass(metaClass) {
@Override
def getAttribute(object, String attribute) {
object.get("@$attribute")
}
@Override
void setAttribute(object, String attribute, newValue) {
object.set("@$attribute", newValue)
}
};
GroovySystem.metaClassRegistry.setMetaClass(nodeClass, newMetaClass);
}
}
def e = new Example()
e.@woo // prints "Getting @woo"
e.@woo = 'yay' // prints "Setting @woo to yay"