【问题标题】:How to set/replace values in xml with Python etree lxml xpath?如何使用 Python etree lxml xpath 设置/替换 xml 中的值?
【发布时间】:2016-08-17 23:50:14
【问题描述】:

我想用 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 不存在,我该如何添加它们?
  • 如何将更改写回文件?

【问题讨论】:

    标签: xml python-2.7 xpath lxml


    【解决方案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 在它的属性上设置新值,其中第一个参数是命名空间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")
    

    新的.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]: 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-04
      • 1970-01-01
      • 2017-07-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多