Search code examples
androidxmlxmlstarlet

Getting attribute value of an element from its specific sub-element


I'm trying to use xmlstarlet on my Mac to get the attribute value of an element from its sub-element, here is an example

I have an xml file with a lot of elements like this, the attributes like android:configChanges vary from element to element:

<activity android:configChanges="ABC" android:screenOrientation="XXX">
        <intent-filter>
            <action android:name="XYZ"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
</activity>

the thing is that the value of category android:name is unique to this element which is android.intent.category.LAUNCHER.

Is there a way using xmllint or xmlstarlet where i run the

xmlstarlet <magic code> <filename>

the <magic code> should be able to tell the value of android:configChanges and android:screenOrientation when it is provided that /activity/intent-filter/category android:name = "android.intent.category.LAUNCHER"

thus, the deisred output should be

android:configChanges="ABC" android screenOrientation="XXX"

so far the only thing i know is how to get the value of other sub-elements from a specific sub-element using

xmlstarlet sel -t -c "/activity/intent-filter/category[@android:name=android.intent.category.LAUNCHER]/*" file.xml

which returns

<action android:name="XYZ"/>
<category android:name="android.intent.category.LAUNCHER"/>

Any help will be appriciated! :)


Solution

  • I made a Shell Script to help me do this :)

    b="$(grep -n "android.intent.category.LAUNCHER" File.xml | cut -f1 -d:)"
    head -n $b File.xml >/tmp/foo.txt
    f="$(awk '/activity/{k=$0}END{print k}' /tmp/foo.txt)"
    echo $f >> /tmp/file.xml
    echo "</activity>" >> /tmp/file.xml
    sed -i -e 's/android:/d/g' /tmp/file.xml
    xmllint --xpath "string(//activity/@dconfigChanges)" /tmp/file.xml
    xmllint --xpath "string(//activity/@dscreenOrientation)" /tmp/file.xml
    

    Since i am using a unix based operating system and not limited to using only xmlstarlet, i made a script that greps the value "android.intent.category.LAUNCHER" and then uses head from that file appending all the lines above "android.intent.category.LAUNCHER" to a new text file. to find the first instance of <activity, the script then uses grep and tail to search from the bottom of the new text file. this returns the full value of <activity android:configChanges="ABC" android:screenOrientation="XXX"> as a string, using echo and >> the string is output as an xml file. since the string doesnt contain a </activity> the script echo's <activity> into the file. sed is used to replace the android: in each attribute with d because : cant be used. using xmllint we can then find the value of the attributes android:configChanges and android:screenOrientation easily.

    Output:

    ABC
    XXX
    

    as expected

    Edit:

    Changed f="$(grep "<activity" /tmp/foo.txt | tail -$b)" to f="$(awk '/activity/{k=$0}END{print k}' /tmp/foo.txt)" the awk command gets the last occurence of activity in the foo.txt file, it works becuase of the head -n $b File.xml >/tmp/foo.txt command before, which only takes the first b lines of the file