如何使用Python etree lxml xpath设置/替换xml中的值?

时间:2016-08-17 23:50:14

标签: xml python-2.7 xpath lxml

我想用Python2.7和etree / lxml / xpath解析AndroidManifest.xml以查找属性名称并获取设置它们的值。

的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>

在Bash上我使用了xmlstarlet:

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

这是我到目前为止所做的:

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)

这将打印true,20然后是整个未更改的xml文档。

  • 如何将allowBackup和minSdkVersion的值分别设置为false和19?

加成:

  • 如果它们不存在,如何添加android:allowBackup或android:minSdkVersion?
  • 如何将更改写回文件?

1 个答案:

答案 0 :(得分: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>"""

要设置值,找到节点然后访问它的attrib dict然后使用QName在其属性上设置新值,其中第一个arg是命名空间URI,第二个是属性名称:

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))

这给了你:

<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>

要从文件读取和写入文件,逻辑是在write传递文件名和任何其他参数的结尾处相同的:

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>

您现在知道如何编写更改,因为缺少的属性按原样工作,如果值存在,我们会更新它,如果不是,我们创建它:

# 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]: