Строка/столбец отчета python о происхождении узла XML

В настоящее время я использую xml.dom.minidom для анализа XML в python. После синтаксического анализа я делаю некоторые отчеты о содержании и хотел бы сообщить строку (и столбец), где начинается тег в исходном XML-документе, но я не понимаю, как это возможно.

Я хотел бы придерживаться xml.dom / xml.dom.minidom, если это возможно, но если мне нужно использовать синтаксический анализатор SAX для получения информации об источнике, я могу сделать это - идеальным в этом случае было бы использование SAX для отслеживания node, но все равно получаю DOM для постобработки.

Любые предложения о том, как это сделать? Надеюсь, я просто что-то упускаю из виду в документах, и это очень просто.


person Jeremy Slade    schedule 25.01.2011    source источник
comment
xmlparser из xml.parsers.expat поддерживает номера строк/столбцов. docs.python.org/library/pyexpat.html   -  person jfs    schedule 25.01.2011
comment
lxml.etree поддерживает номера строк. codespeak.net/lxml   -  person jfs    schedule 25.01.2011


Ответы (2)


Путем внесения исправлений в обработчик содержимого 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
person aknuds1    schedule 27.02.2011

Другой способ обойти эту проблему — добавить информацию о номере строки в документ перед его синтаксическим анализом. Вот идея:

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 заключается в том, что оно не требует вмешательства во внутренние органы минидома.

person Tfry    schedule 08.12.2014