Как определить пространство имен данного атрибута внутри NodeChild - PullRequest
4 голосов
/ 02 февраля 2012

Я пытаюсь проанализировать некоторые Android XML с помощью XmlSlurper.Для данного дочернего узла я хочу определить, был ли указан атрибут с определенным пространством имен.

Например, в следующем 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 ]

4 голосов
/ 02 февраля 2012

Вы можете использовать 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)

namespaceTagHints является свойством GPathResult , который является суперклассом 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"}"
  }
}
0 голосов
/ 03 февраля 2012

Единственное решение, которое я нашел до сих пор, заключается в том, чтобы вернуться к рефлексии.

Как указывал Дамо выше, NodeChild содержит свойство Node, а внутри Node находится карта attributeNamespaces, в которую мне нужно попасть.

Класс Node не предоставляет это свойство (как это происходит с attributes()), и кажется, что оно используется только внутри метода build(). Чёрт.

Получив узел, я поэтому вызываю:

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

Это работает, хотя это не так Отличный .

...