使用 Python 向 Plist 中的深层节点添加数据
Adding data to deep node in Plist with Python
我有一个 plist,我最初从中提取信息然后需要向其中写入信息。使用 xml.etree.ElementTree,我可以访问 "WUT",但如果我尝试用数字“1”覆盖它或在它之后添加另一行“2”,则使用以下方法:
ET.SubElement(plist[0][15][1][1][1][1][1][1][1], '1')
或与
plist[0][15][1][1][1][1][1][1][1].append('1')
我会得到一个索引越界错误,因为显然节点 1 还不存在。
我需要添加到特定节点(并且在大多数情况下创建该节点)。在 WUT 下面,我想添加大约 200 个带有随机信息的字符串子项(现在假设为 1-200)。我将如何使用 plistlib 或 xml.etree.ElementTree.
执行此操作
<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
<dict>
<key>82</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
<dict>
<key>64</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
我想达到的结果是:
<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>1</string>
<string>2</string>
<string>3</string>
<string>4</string>
<string>5</string>
<string>6</string>
<string>7</string>
</array>
</dict>
</dict>
<dict>
<key>82</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
<dict>
<key>64</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
*编辑这些是我试图进入正确位置的 for 循环。它似乎只适用于最后一个,但这意味着它将把我的新数据放入 "Writing" 而不是 "Notes"。
for plist_title in tree.xpath('//dict[key="Notes"][1]')
for plist_tester in plist_title.xpath('//dict[key="13"][1]')
plist_tester.insert(1,myData)
如果我想让它进入写作,我会做类似的事情:
for plist_title in tree.xpath('//dict[key="Writing"][1]')
for plist_tester in plist_title.xpath('//dict[key="13"][1]')
plist_tester.insert(1,myData)
或不同的测试员级别:
for plist_title in tree.xpath('//dict[key="Notes"][1]')
for plist_tester in plist_title.xpath('//dict[key="82"][1]')
plist_tester.insert(1,myData)
遗憾的是它们不起作用:(
使用 lxml 和一些 xpath 魔法:
from lxml import etree
from lxml.builder import E
plist = """
<root>
<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
</array>
</dict>
</dict>
</dict>
</root>"""
这将创建一个 lxml 对象(您也可以从文件加载它):
xml_data = etree.fromstring(plist)
创建候选元素数组:
array = E('array')
for num in range(10):
array.append(E.string(str(num)))
这是它的样子:
print(etree.tostring(array, pretty_print=True))
<array>
<string>0</string>
<string>1</string>
<string>2</string>
<string>3</string>
<string>4</string>
<string>5</string>
<string>6</string>
<string>7</string>
<string>8</string>
<string>9</string>
</array>
从主 plist 中,获取有趣的条目,在本例中是第一个包含 WUT
的数组,将其删除并附加您想要的标签:
for first_array_contains_wut in xml_data.xpath('//array[string="WUT"][1]'):
parent_tag = first_array_contains_wut.getparent()
parent_tag.remove(first_array_contains_wut)
parent_tag.append(array)
下面是修改后的最终版本:
print(etree.tostring(xml_data, pretty_print=True))
<root>
<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array><string>0</string><string>1</string><string>2</string><string>3</string><string>4</string><string>5</string><string>6</string><string>7</string><string>8</string><string>9</string></array></dict>
</dict>
</array>
</dict>
</dict>
</dict>
</root>
来自评论:
If the key "Notes" could change around to "Writing" in the same plist, is there a way to use xpath to search for both "Writing" and the "13" key? If both Notes and writing had 13, there is no way i could be sure i have found it. I tried with 2x for loops, but i always end up with the second "13".
您可以尝试更明确的匹配,如果这是我理解您的节点的样子:
<root>
<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
</array>
</dict>
<dict>
<key>Writing</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>WUT2</string>
</array>
</dict>
</dict>
</array>
</dict>
</dict>
</dict>
</dict>
</root>
如果我想得到 'WUT2' 是在有 'Writing' 的字典下,你可以这样做:
In [26]: [x.text for x in xml_data.xpath('//dict[key/text()="Writing"]//string')]
Out[26]: ['WUT2']
但是,如果您在第二个“13”键之后,那么您可以执行以下操作:
In [35]: [x.text for x in xml_data.xpath('//dict[//key[text()="13"]][2]//string')]
Out[35]: ['WUT2']
我有一个 plist,我最初从中提取信息然后需要向其中写入信息。使用 xml.etree.ElementTree,我可以访问 "WUT",但如果我尝试用数字“1”覆盖它或在它之后添加另一行“2”,则使用以下方法:
ET.SubElement(plist[0][15][1][1][1][1][1][1][1], '1')
或与
plist[0][15][1][1][1][1][1][1][1].append('1')
我会得到一个索引越界错误,因为显然节点 1 还不存在。
我需要添加到特定节点(并且在大多数情况下创建该节点)。在 WUT 下面,我想添加大约 200 个带有随机信息的字符串子项(现在假设为 1-200)。我将如何使用 plistlib 或 xml.etree.ElementTree.
执行此操作<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
<dict>
<key>82</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
<dict>
<key>64</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
我想达到的结果是:
<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>1</string>
<string>2</string>
<string>3</string>
<string>4</string>
<string>5</string>
<string>6</string>
<string>7</string>
</array>
</dict>
</dict>
<dict>
<key>82</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
<dict>
<key>64</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
*编辑这些是我试图进入正确位置的 for 循环。它似乎只适用于最后一个,但这意味着它将把我的新数据放入 "Writing" 而不是 "Notes"。
for plist_title in tree.xpath('//dict[key="Notes"][1]')
for plist_tester in plist_title.xpath('//dict[key="13"][1]')
plist_tester.insert(1,myData)
如果我想让它进入写作,我会做类似的事情:
for plist_title in tree.xpath('//dict[key="Writing"][1]')
for plist_tester in plist_title.xpath('//dict[key="13"][1]')
plist_tester.insert(1,myData)
或不同的测试员级别:
for plist_title in tree.xpath('//dict[key="Notes"][1]')
for plist_tester in plist_title.xpath('//dict[key="82"][1]')
plist_tester.insert(1,myData)
遗憾的是它们不起作用:(
使用 lxml 和一些 xpath 魔法:
from lxml import etree
from lxml.builder import E
plist = """
<root>
<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
</array>
</dict>
</dict>
</dict>
</root>"""
这将创建一个 lxml 对象(您也可以从文件加载它):
xml_data = etree.fromstring(plist)
创建候选元素数组:
array = E('array')
for num in range(10):
array.append(E.string(str(num)))
这是它的样子:
print(etree.tostring(array, pretty_print=True))
<array>
<string>0</string>
<string>1</string>
<string>2</string>
<string>3</string>
<string>4</string>
<string>5</string>
<string>6</string>
<string>7</string>
<string>8</string>
<string>9</string>
</array>
从主 plist 中,获取有趣的条目,在本例中是第一个包含 WUT
的数组,将其删除并附加您想要的标签:
for first_array_contains_wut in xml_data.xpath('//array[string="WUT"][1]'):
parent_tag = first_array_contains_wut.getparent()
parent_tag.remove(first_array_contains_wut)
parent_tag.append(array)
下面是修改后的最终版本:
print(etree.tostring(xml_data, pretty_print=True))
<root>
<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array><string>0</string><string>1</string><string>2</string><string>3</string><string>4</string><string>5</string><string>6</string><string>7</string><string>8</string><string>9</string></array></dict>
</dict>
</array>
</dict>
</dict>
</dict>
</root>
来自评论:
If the key "Notes" could change around to "Writing" in the same plist, is there a way to use xpath to search for both "Writing" and the "13" key? If both Notes and writing had 13, there is no way i could be sure i have found it. I tried with 2x for loops, but i always end up with the second "13".
您可以尝试更明确的匹配,如果这是我理解您的节点的样子:
<root>
<key>Title</key>
<dict>
<key>Set</key>
<dict>
<key>Notes</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>WUT</string>
</array>
</dict>
</dict>
</array>
</dict>
<dict>
<key>Writing</key>
<dict>
<key>Tester</key>
<array>
<dict>
<key>13</key>
<dict>
<key>Param</key>
<array>
<string>WUT2</string>
</array>
</dict>
</dict>
</array>
</dict>
</dict>
</dict>
</dict>
</root>
如果我想得到 'WUT2' 是在有 'Writing' 的字典下,你可以这样做:
In [26]: [x.text for x in xml_data.xpath('//dict[key/text()="Writing"]//string')]
Out[26]: ['WUT2']
但是,如果您在第二个“13”键之后,那么您可以执行以下操作:
In [35]: [x.text for x in xml_data.xpath('//dict[//key[text()="13"]][2]//string')]
Out[35]: ['WUT2']