Reputation: 351
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.
Bonus:
Upvotes: 1
Views: 3192
Reputation: 180391
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]:
Upvotes: 1