Groovy中的动态直接字段访问?

问题描述 投票:0回答:1

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

Outputs:

Hi
bar

据我所知: xml.foobar (一个不存在的属性)可以通过使用元编程方法来处理 propertyMissing() (类似于 methodMissing() 为不存在的方法)。) 然而,我似乎找不到一个动态的类似于直接访问字段的方法,如 foo. 我怎样才能实现类似的东西?也就是说,我可以创建一个类,动态处理属性方法的访问(例如,连同一个支持映射)和上面的元编程方法,但似乎没有等价的字段,例如。

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类型的元编程 "字段"(也可以使用 xml.getProperty('foobar'), xml['foobar']xml.getAt('foobar')),并且 xml.@foo 是一个普通的、类似Java的字段。如果上面的问题有什么固有的误解,请告诉我。

groovy dynamic metaprogramming
1个回答
1
投票

所以,你可以去看一下源码中的 Node 这边

神奇的是在静态初始化块上的 第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"
© www.soinside.com 2019 - 2024. All rights reserved.