我正在使用Python(minidom)来解析XML文件,该文件打印出类似于此的层次结构(此处使用缩进来显示重要的层次关系):
My Document
Overview
Basic Features
About This Software
Platforms Supported
相反,程序在节点上多次迭代并生成以下内容,打印重复节点。 (在每次迭代时查看节点列表,很明显为什么它会这样做但我似乎无法找到获取我正在寻找的节点列表的方法。)
My Document
Overview
Basic Features
About This Software
Platforms Supported
Basic Features
About This Software
Platforms Supported
Platforms Supported
这是XML源文件:
<?xml version="1.0" encoding="UTF-8"?>
<DOCMAP>
<Topic Target="ALL">
<Title>My Document</Title>
</Topic>
<Topic Target="ALL">
<Title>Overview</Title>
<Topic Target="ALL">
<Title>Basic Features</Title>
</Topic>
<Topic Target="ALL">
<Title>About This Software</Title>
<Topic Target="ALL">
<Title>Platforms Supported</Title>
</Topic>
</Topic>
</Topic>
</DOCMAP>
这是Python程序:
import xml.dom.minidom
from xml.dom.minidom import Node
dom = xml.dom.minidom.parse("test.xml")
Topic=dom.getElementsByTagName('Topic')
i = 0
for node in Topic:
alist=node.getElementsByTagName('Title')
for a in alist:
Title= a.firstChild.data
print Title
我可以通过不将“Topic”元素嵌套,通过将较低级别的主题名称更改为“SubTopic1”和“SubTopic2”来解决问题。但是,我希望利用内置的XML层次结构,而不需要不同的元素名称;似乎我应该能够嵌套“主题”元素,并且应该有一些方法来了解我目前正在查看哪个级别的“主题”。
我尝试了许多不同的XPath功能但没有取得多大成功。
答案 0 :(得分:9)
getElementsByTagName是递归的,您将获得具有匹配tagName的所有后代。由于您的主题包含也具有标题的其他主题,因此调用将多次获得较低的标题。
如果您只想要所有匹配的直接子项,并且您没有可用的XPath,您可以编写一个简单的过滤器,例如:
def getChildrenByTagName(node, tagName):
for child in node.childNodes:
if child.nodeType==child.ELEMENT_NODE and (tagName=='*' or child.tagName==tagName):
yield child
for topic in document.getElementsByTagName('Topic'):
title= list(getChildrenByTagName('Title'))[0] # or just get(...).next()
print title.firstChild.data
答案 1 :(得分:7)
让我在这里发表评论......
感谢您的尝试。它没有用,但它给了我一些想法。以下工作(相同的一般想法; FWIW,nodeType是ELEMENT_NODE):
import xml.dom.minidom
from xml.dom.minidom import Node
dom = xml.dom.minidom.parse("docmap.xml")
def getChildrenByTitle(node):
for child in node.childNodes:
if child.localName=='Title':
yield child
Topic=dom.getElementsByTagName('Topic')
for node in Topic:
alist=getChildrenByTitle(node)
for a in alist:
# Title= a.firstChild.data
Title= a.childNodes[0].nodeValue
print Title
答案 2 :(得分:4)
我认为可以提供帮助
import os
import sys
import subprocess
import base64,xml.dom.minidom
from xml.dom.minidom import Node
f = open("file.xml",'r')
data = f.read()
i = 0
doc = xml.dom.minidom.parseString(data)
for topic in doc.getElementsByTagName('Topic'):
title= doc.getElementsByTagName('Title')[i].firstChild.nodeValue
print title
i +=1
<强>输出:强>
My Document
Overview
Basic Features
About This Software
Platforms Supported
答案 3 :(得分:3)
您可以使用以下生成器运行列表并获取具有缩进级别的标题:
def f(elem, level=-1):
if elem.nodeName == "Title":
yield elem.childNodes[0].nodeValue, level
elif elem.nodeType == elem.ELEMENT_NODE:
for child in elem.childNodes:
for e, l in f(child, level + 1):
yield e, l
如果您使用您的文件进行测试:
import xml.dom.minidom as minidom
doc = minidom.parse("test.xml")
list(f(doc))
您将获得包含以下元组的列表:
(u'My Document', 1),
(u'Overview', 1),
(u'Basic Features', 2),
(u'About This Software', 2),
(u'Platforms Supported', 3)
当然,微调是一个基本的想法。如果您只想在开头使用空格,则可以直接在生成器中进行编码,但是使用级别可以获得更大的灵活性。你也可以自动检测第一级(这里只是将级别初始化为-1的糟糕工作......)。
答案 4 :(得分:2)
递归函数:
import xml.dom.minidom
def traverseTree(document, depth=0):
tag = document.tagName
for child in document.childNodes:
if child.nodeType == child.TEXT_NODE:
if document.tagName == 'Title':
print depth*' ', child.data
if child.nodeType == xml.dom.Node.ELEMENT_NODE:
traverseTree(child, depth+1)
filename = 'sample.xml'
dom = xml.dom.minidom.parse(filename)
traverseTree(dom.documentElement)
你的xml:
<?xml version="1.0" encoding="UTF-8"?>
<DOCMAP>
<Topic Target="ALL">
<Title>My Document</Title>
</Topic>
<Topic Target="ALL">
<Title>Overview</Title>
<Topic Target="ALL">
<Title>Basic Features</Title>
</Topic>
<Topic Target="ALL">
<Title>About This Software</Title>
<Topic Target="ALL">
<Title>Platforms Supported</Title>
</Topic>
</Topic>
</Topic>
</DOCMAP>
您想要的输出:
$ python parse_sample.py
My Document
Overview
Basic Features
About This Software
Platforms Supported