How to define the namespace of this attribute inside NodeChild

I am trying to XmlSlurper some Android XML using XmlSlurper . For this child node, I want to determine if an attribute with a specific namespace has been specified.

For example, in the following XML, I would like to know if the EditText node had any attributes from the declared namespace 'b':

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:b="http://xyzcom"> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" b:enabled="true" /> </LinearLayout> 

I'll start by calling:

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

to get the GPathResult root GPathResult . When I repeat children, I am provided with an instance of groovy.util.slurpersupport.NodeChild . In this class, I can check the attributes by calling attributes() , and in the case of EditText above, this will return the following map: [layout_width: "fill_parent", layout_height: "wrap_content", enabled: "true"] .

This is good and good. However, there seems to be no way to query the namespace of this attribute. Did I miss something?

+4
source share
2 answers

You can use XmlParser and not XmlSlurper and do this:

 def xml = '''<LinearLayout | xmlns:android="http://schemas.android.com/apk/res/android" | xmlns:b="http://xyzcom"> | | <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" } 

What prints

 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://xyzcom) = true 

Edit

To use XmlSlurper, you first need to access the namespaceTagHints property from the root of the node using reflection:

 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 is a GPathResult property that is a superclass of NodeChild .

You can then cross-reference this map to access the namespace prefix and print the same result as above:

 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"}" } } 
+4
source

The only solution I have found so far is to return to reflection.

As Damo noted, NodeChild contains the Node property, and inside Node is the attributeNamespaces map I need to get.

The Node class does not expose this property (as it does with attributes() ), and it seems to be used only in the build() method. Heck.

Having received node, I call:

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

It works, although it doesn't feel so groovy.

0
source

Source: https://habr.com/ru/post/1394296/


All Articles