如何确定NodeChild中给定属性的命名空间

时间:2012-02-01 22:43:30

标签: groovy xml-parsing

我正在尝试使用XmlSlurper解析一些Android XML。对于给定的子节点,我想检测是否已指定具有特定命名空间的属性。

例如,在以下XML中,我想知道EditText节点是否具有声明的'b'命名空间中的任何属性:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:b="http://x.y.z.com">

    <EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        b:enabled="true" />

</LinearLayout>

我先致电:

 def rootNode = new XmlSlurper().parseText(text)

获取根GPathResult的句柄。当我遍历孩子时,我会得到一个groovy.util.slurpersupport.NodeChild的实例。在这个课程中,我可以通过调用attributes()来检查属性,如果是EditText,则会返回以下地图:[layout_width: "fill_parent", layout_height: "wrap_content", enabled: "true"]

这一切都很好。但是,似乎没有办法查询给定属性的命名空间。我在这里错过了什么吗?

2 个答案:

答案 0 :(得分:4)

您可以使用XmlParser而不是XmlSlurper并执行此操作:

def xml = '''<LinearLayout
            |    xmlns:android="http://schemas.android.com/apk/res/android"
            |    xmlns:b="http://x.y.z.com">
            |
            |  <EditText
            |      android:layout_width="fill_parent"
            |      android:layout_height="wrap_content"
            |      b:enabled="true" />
            |</LinearLayout>'''.stripMargin()

def root = new XmlParser().parseText( xml )

root.EditText*.attributes()*.each { k, v ->
  println "$k.localPart $k.prefix($k.namespaceURI) = $v"
}

打印出来

layout_width android(http://schemas.android.com/apk/res/android) = fill_parent
layout_height android(http://schemas.android.com/apk/res/android) = wrap_content
enabled b(http://x.y.z.com) = true

修改

要使用XmlSlurper,首先需要使用反射从根节点访问namespaceTagHints属性:

def rootNode = new XmlSlurper().parseText(xml)

def xmlClass = rootNode.getClass()
def gpathClass = xmlClass.getSuperclass()
def namespaceTagHintsField = gpathClass.getDeclaredField("namespaceTagHints")
namespaceTagHintsField.setAccessible(true)

def namespaceDeclarations = namespaceTagHintsField.get(rootNode)

namespaceTagHintsGPathResult的属性,是NodeChild的超类。

然后,您可以交叉引用此映射以访问命名空间前缀,并打印出与上面相同的结果:

rootNode.EditText.nodeIterator().each { groovy.util.slurpersupport.Node n ->
  n.@attributeNamespaces.each { name, ns ->
    def prefix = namespaceDeclarations.find {key, value -> value == ns}.key
    println "$name $prefix($ns) = ${n.attributes()"$name"}"
  }
}

答案 1 :(得分:0)

到目前为止,我找到的唯一解决办法就是反思。

正如Damo在上面指出的那样,NodeChild包含Node属性,Node内部是我需要获得的attributeNamespaces地图。

Node类不公开此属性(与attributes()一样),并且它似乎只在build()方法中使用。织补。

检索完节点后,我打电话给:

def attributeNamespacesField = node.getClass().getDeclaredField("attributeNamespaces")
attributeNamespacesField.setAccessible(true)
def attributeNamespacesMap = attributeNamespacesField.get(node)

它有效,但感觉不是 Groovy