1

I want to parse an AndroidManifest.xml with Python2.7 and etree/lxml/xpath to find attribute names and to get and set their values.

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.wearable.timer" >
<uses-sdk android:minSdkVersion="20"
          android:targetSdkVersion="22" />
<application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.DeviceDefault.Light"
        android:allowBackup="true">
</application>
</manifest>

On Bash I used xmlstarlet for this:

xmlstarlet ed --inplace -u '/manifest/application/@android:allowBackup' -v true AndroidManifest.xml

This is what I have so far:

from lxml import etree
NS = {'android' : 'http://schemas.android.com/apk/res/android'}
tree = etree.parse('AndroidManifest.xml')
# get values
print tree.xpath("///@android:allowBackup", namespaces=NS)[0]
print tree.xpath("///@android:minSdkVersion", namespaces=NS)[0]
# set values ?
# write changes back to file ?
print etree.tostring(tree, encoding='utf-8', pretty_print=True)

This prints true, 20 and then the whole unchanged xml document.

  • How do I set the values for allowBackup and minSdkVersion to false and 19, respectively?

Bonus:

  • How do I add android:allowBackup or android:minSdkVersion if they are not already present?
  • How do I write the changes back to file?

1 Answer 1

1
xml = """<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.wearable.timer" >
<uses-sdk android:minSdkVersion="20"
          android:targetSdkVersion="22" />
<application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.DeviceDefault.Light"
        android:allowBackup="true">
</application>
</manifest>"""

To set the values, find the node then access it's attrib dict then set the new value on it's attribute by using QName where the first arg is the namespace URI and the second is the attribute name:

import lxml.etree as et

tree = et.fromstring(xml)
# holds namespace mappings
nsmap = tree.nsmap
# get prefix URI
android = tree.nsmap["android"]

# find app and set the attribute value.
tree.find("application", nsmap).attrib[et.QName(android, "allowBackup")] = "false"
tree.find("uses-sdk", nsmap).attrib[et.QName(android, "minSdkVersion")] = "17"
print(et.tostring(tree, pretty_print=True))

Which gives you:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.wearable.timer">
<uses-sdk android:minSdkVersion="17" android:targetSdkVersion="22"/>
<application android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault.Light" android:allowBackup="false">
</application>
</manifest>

To read and write from and to a file, the logic is the same just at the end you write passing in the filename and any other args:

import lxml.etree as et
from StringIO import StringIO

tree = et.parse(StringIO(xml))
root = tree.getroot()
nsmap = root.nsmap
android = nsmap["android"]

tree.find("application", nsmap).attrib[et.QName(android, "allowBackup")] = "false"
tree.find("uses-sdk", nsmap).attrib[et.QName(android, "minSdkVersion")] = "17"

# write the updated html to "new.xml"
tree.write("new.xml", encoding="utf-8")

new.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.wearable.timer">
<uses-sdk android:minSdkVersion="17" android:targetSdkVersion="22"/>
<application android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault.Light" android:allowBackup="false">
</application>
</manifest>

You now know how to write the changes, as for missing attributes it works as is, if the value exists we update it, if not we create it:

# no minSDk...
In [31]: !cat  test.xml<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.wearable.timer" >
<uses-sdk  android:targetSdkVersion="22" />
<application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.DeviceDefault.Light"
        android:allowBackup="true">
</application>
</manifest>
In [32]: tree = et.parse("test.xml")

In [33]: root = tree.getroot()

In [34]: nsmap = root.nsmap

In [35]: android = nsmap["android"]

In [36]: tree.find("uses-sdk", nsmap).attrib[et.QName(android, "minSdkVersion")] = "17"

In [37]: tree.write("test.xml", encoding="utf-8")

# New attribute and value created.
In [38]: !cat  test.xml<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.wearable.timer">
<uses-sdk android:targetSdkVersion="22" android:minSdkVersion="17"/>
<application android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault.Light" android:allowBackup="true">
</application>
</manifest>
In [39]: 
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.