Searching for xpath expression with colon in attribute name throws exception (node.js elementtree module)
Asked Answered
D

2

3

Using the elementtree package in nodejs, I'm trying to verify the existence of a certain xml attribute in an xml file (specifically an android manifest file).

var manifestTxt = fs.readFileSync('AndroidManifest.xml', 'utf-8'),
    manifestDoc = new et.ElementTree(et.XML(manifestTxt)),
    expected = 'application/activity[@android:name="com.whatever.app"]';

test.ok(manifestDoc.find(expected));

I'm getting the following exception:

node_modules/elementtree/lib/elementpath.js:210
      throw new SyntaxError(token, 'Invalid attribute predicate');
        ^
Error: Invalid attribute predicate

It doesn't seem to like the colon in the attribute name, but without it the search doesn't match. I think I'm handling the namespace wrong -- but can't find the proper way.

Edit Here's the sample xml I'm searching:

<?xml version='1.0' encoding='utf-8'?>
<manifest ... xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:debuggable="true" android:icon="@drawable/icon" android:label="@string/app_name">&#xA; 
        <activity android:label="@string/app_name" android:name="com.whatever.app">&#xA;                
            <intent-filter>&#xA;</intent-filter>
        </activity> 
    </application>
    <uses-sdk android:minSdkVersion="5" />
</manifest>
Derivative answered 20/6, 2012 at 21:33 Comment(1)
Have you tried escaping the colon \:?Mccown
C
2

Elementtree expects namespace URIs, not namespace prefixes.

var manifestTxt = fs.readFileSync('AndroidManifest.xml', 'utf-8'),
    manifestDoc = new et.ElementTree(et.XML(manifestTxt)),
    expected = '//application/activity[@{http://schemas.android.com/apk/res/android}name="com.whatever.app"]';

test.ok( manifestDoc.find(expected) );

See: ElementTree: Working with Qualified Names


Edit The XPath implementation of node-elementtree does not currently seem to have namespace support at all.

Missing that you'd have to do some legwork:

var manifestTxt = fs.readFileSync('AndroidManifest.xml', 'utf-8'),
    manifestDoc = new et.ElementTree(et.XML(manifestTxt)),
    activities = manifestDoc.findall('//application/activity'), i;

for (i=0; i<activities.length; i++) {
  if ( activities[i].attrib['android:name'] === 'com.whatever.app' ) {
    test.ok(true);
  }
}

The line if ( activities[i].attrib['android:name'] === 'com.whatever.app' ) { is largely a guess.

I don't know how the parser handles namespaced attributes. When in doubt, just dump the whole activities[i].attrib to the console and see what the parser did. Adapt the above code accordingly. I'm afraid that's as close as you will get with that kind of limited XPath support.

Calley answered 20/6, 2012 at 21:59 Comment(4)
Thanks, I did try that, and though it no longer threw the exception, the find failed (so I figured it was also wrong). I've added the sample xml that I'm searching. So the question then becomes, why does the find fail?Derivative
@DimitreNovatchev That's right. Anyway, that's how ElementTree works as per the documentation (see the "Working with qualified names" link above). I can't do very much about it.Calley
@Derivative Looking at the source, I don't see any indication that node-elementtree even has namespace support. The XPath implementation is extremely basic. There is no function support, either. Can you confirm that this is the version you're using?Calley
Thats the version I'm using, yes. So you're probably right about the lack of namespace support. Thanks.Derivative
R
8

In case you don't have the information how to register the namespace and use the associated prefix for it, use:

application/activity
   [@*[local-name()=name' 
     and 
      namespace-uri() = 'http://schemas.android.com/apk/res/android'
      ] 
   = 
    'com.whatever.app'
   ]

Simpler expressions that aren't safe in the general case, but may select the wanted node(s) in this specific case:

application/activity[@*[local-name()='name'] = 'com.whatever.app']

or this expression:

application/activity[@*[name()='android:name'] = 'com.whatever.app']
Roldan answered 21/6, 2012 at 4:55 Comment(0)
C
2

Elementtree expects namespace URIs, not namespace prefixes.

var manifestTxt = fs.readFileSync('AndroidManifest.xml', 'utf-8'),
    manifestDoc = new et.ElementTree(et.XML(manifestTxt)),
    expected = '//application/activity[@{http://schemas.android.com/apk/res/android}name="com.whatever.app"]';

test.ok( manifestDoc.find(expected) );

See: ElementTree: Working with Qualified Names


Edit The XPath implementation of node-elementtree does not currently seem to have namespace support at all.

Missing that you'd have to do some legwork:

var manifestTxt = fs.readFileSync('AndroidManifest.xml', 'utf-8'),
    manifestDoc = new et.ElementTree(et.XML(manifestTxt)),
    activities = manifestDoc.findall('//application/activity'), i;

for (i=0; i<activities.length; i++) {
  if ( activities[i].attrib['android:name'] === 'com.whatever.app' ) {
    test.ok(true);
  }
}

The line if ( activities[i].attrib['android:name'] === 'com.whatever.app' ) { is largely a guess.

I don't know how the parser handles namespaced attributes. When in doubt, just dump the whole activities[i].attrib to the console and see what the parser did. Adapt the above code accordingly. I'm afraid that's as close as you will get with that kind of limited XPath support.

Calley answered 20/6, 2012 at 21:59 Comment(4)
Thanks, I did try that, and though it no longer threw the exception, the find failed (so I figured it was also wrong). I've added the sample xml that I'm searching. So the question then becomes, why does the find fail?Derivative
@DimitreNovatchev That's right. Anyway, that's how ElementTree works as per the documentation (see the "Working with qualified names" link above). I can't do very much about it.Calley
@Derivative Looking at the source, I don't see any indication that node-elementtree even has namespace support. The XPath implementation is extremely basic. There is no function support, either. Can you confirm that this is the version you're using?Calley
Thats the version I'm using, yes. So you're probably right about the lack of namespace support. Thanks.Derivative

© 2022 - 2024 — McMap. All rights reserved.