python 报告 XML 节点的行/列来源

6 投票
2 回答
3927 浏览
提问于 2025-04-16 10:33

我现在在用python的xml.dom.minidom来解析一些XML文件。解析完之后,我想对内容进行一些报告,特别是想报告一下标签在源XML文档中开始的位置(行和列),但是我不知道怎么做到这一点。

如果可以的话,我希望继续使用xml.dom / xml.dom.minidom,但如果需要用SAX解析器来获取这些位置信息,我也可以这样做——理想情况下,我希望用SAX来追踪节点的位置,但最后还是能得到一个DOM来进行后续处理。

有没有什么建议可以做到这一点?希望我只是没有在文档中找到相关内容,这个问题其实很简单。

2 个回答

0

解决这个问题的另一种方法是在解析文档之前,把行号信息添加到文档里。大概是这个意思:

LINE_DUMMY_ATTR = '_DUMMY_LINE' # Make sure this string is unique!
def parseXml(filename):
  f = file.open(filename, 'r')
  l = 0
  content = list ()
  for line in f:
    l += 1
    content.append(re.sub(r'<(\w+)', r'<\1 ' + LINE_DUMMY_ATTR + '="' + str(l) + '"', line))
  f.close ()

  return minidom.parseString ("".join(content))

然后你可以用下面的方式获取某个元素的行号:

int (element.getAttribute (LINE_DUMMY_ATTR))

很明显,这种方法也有自己的缺点。如果你真的需要列号的话,添加列号的过程会复杂一些。而且,如果你想提取文本节点或者注释,或者使用 Node.toXml(),你需要确保把任何意外匹配的 LINE_DUMMY_ATTR 去掉。

这个方法相比 aknuds1 的答案有一个好处,就是它不需要去修改 minidom 的内部结构。

5

通过对minidom内容处理器进行“猴子补丁”,我能够记录每个节点的行号和列号(作为'parse_position'属性)。这有点不太规范,但我找不到任何“官方认可”的方法来做到这一点 :) 这是我的测试脚本:

from xml.dom import minidom
import xml.sax

doc = """\
<File>
  <name>Name</name>
  <pos>./</pos>
</File>
"""


def set_content_handler(dom_handler):
    def startElementNS(name, tagName, attrs):
        orig_start_cb(name, tagName, attrs)
        cur_elem = dom_handler.elementStack[-1]
        cur_elem.parse_position = (
            parser._parser.CurrentLineNumber,
            parser._parser.CurrentColumnNumber
        )

    orig_start_cb = dom_handler.startElementNS
    dom_handler.startElementNS = startElementNS
    orig_set_content_handler(dom_handler)

parser = xml.sax.make_parser()
orig_set_content_handler = parser.setContentHandler
parser.setContentHandler = set_content_handler

dom = minidom.parseString(doc, parser)
pos = dom.firstChild.parse_position
print("Parent: '{0}' at {1}:{2}".format(
    dom.firstChild.localName, pos[0], pos[1]))
for child in dom.firstChild.childNodes:
    if child.localName is None:
        continue
    pos = child.parse_position
    print "Child: '{0}' at {1}:{2}".format(child.localName, pos[0], pos[1])

它输出了以下内容:

Parent: 'File' at 1:0
Child: 'name' at 2:2
Child: 'pos' at 3:2

撰写回答