2013-03-18 2 views
2

Задача состоит в анализе простого XML-документа и анализе содержимого по номеру строки.xml.sax парсер и номера строк и т. Д.

Правильный пакет Python выглядит xml.sax. Но как его использовать?

После некоторого копания в документации, я нашел:

  • Интерфейс xmlreader.Locator имеет информацию: getLineNumber().
  • Интерфейс handler.ContentHandler имеет setDocumentHandler().

Первая мысль была бы создать Locator, передать это ContentHandler, и прочитать информацию выключения локатора во время звонков на свои character() методов и т.д.

НО, xmlreader.Locator только интерфейс скелета , и может только возвращать -1 из любого из своих методов. Так как плохой пользователь, ЧТО я должен делать, если не написать целых Parser и Locator моих собственных ??

Я отвечу на свой вопрос в настоящее время.


(Ну я бы, для произвольного, раздражающих правило, что говорит, что я не могу, за исключением.)


я не смог понять это, используя существующую документацию (или веб поисков) и был вынужден прочитать исходный код для xml.sax (под /usr/lib/python2.7/xml/sax/ в моей системе).

xml.sax Функция make_parser() по умолчанию создает реальный Parser, но что это за штука?
В исходном коде установлено, что это ExpatParser, определенный в файле expatreader.py. И ... у него есть свои Locator, ExpatLocator. Но доступ к этой вещи отсутствует. Между этим и решением возникла большая царапина.

  1. написать свой собственный ContentHandler, который знает о Locato г, и использует его для определения номера строки
  2. создать ExpatParser с xml.sax.make_parser()
  3. создать ExpatLocator, передавая ему экземпляр ExpatParser.
  4. сделать ContentHandler, придавая ему это ExpatLocator
  5. передать ContentHandler в setContentHandler()
  6. вызова парсера parse() на Parser.

Например:

import sys 
import xml.sax 

class EltHandler(xml.sax.handler.ContentHandler): 
    def __init__(self, locator): 
     xml.sax.handler.ContentHandler.__init__(self) 
     self.loc = locator 
     self.setDocumentLocator(self.loc) 

    def startElement(self, name, attrs): pass 

    def endElement(self, name): pass 

    def characters(self, data): 
     lineNo = self.loc.getLineNumber() 
     print >> sys.stdout, "LINE", lineNo, data 

def spit_lines(filepath): 
    try: 
     parser = xml.sax.make_parser() 
     locator = xml.sax.expatreader.ExpatLocator(parser) 
     handler = EltHandler(locator) 
     parser.setContentHandler(handler) 
     parser.parse(filepath) 
    except IOError as e: 
     print >> sys.stderr, e 

if len(sys.argv) > 1: 
    filepath = sys.argv[1] 
    spit_lines(filepath) 
else: 
    print >> sys.stderr, "Try providing a path to an XML file." 

Мартейн Питерс указывает ниже другого подхода с некоторыми преимуществами. Если инициализатор суперкласса ContentHandler правильно вызывается, , тогда оказывается частным, недокументированным членом ._locator является набор, который должен содержать надлежащее Locator.

Преимущество: вам не нужно создавать свои собственные Locator (или узнать, как его создать). Недостаток: он нигде не задокументирован, а использование недокументированной частной переменной неаккуратно.

Thanks Martijn!

ответ

4

Саксовый анализатор сам должен предоставить вашему обработчику контента локатор. Локатор должен реализовать определенные методы, но он может быть любым объектом, если он имеет правильные методы. xml.sax.xmlreader.Locator class - это интерфейс , который предполагается реализовать локатором; если анализатор предоставил объект-локатор вашему обработчику, тогда вы можете рассчитывать на те 4 метода, которые присутствуют в локаторе.

Парсер только поощрял установить локатор, это не требуется. Этот синтаксический анализатор expat XML предоставляет его.

Если подкласс xml.sax.handler.ContentHandler() то он будет предоставлять стандартный setDocumentHandler() метод для вас, и к тому времени .startDocument() на обработчик называется ваш экземпляр обработчика содержимого будет иметь self._locator набор:

from xml.sax.handler import ContentHandler 

class MyContentHandler(ContentHandler): 
    def __init__(self): 
     ContentHandler.__init__(self) 
     # initialize your handler 

    def startElement(self, name, attrs): 
     loc = self._locator 
     if loc is not None: 
      line, col = loc.getLineNumber(), loc.getColumnNumber() 
     else: 
      line, col = 'unknown', 'unknown' 
     print 'start of {} element at line {}, column {}'.format(name, line, col) 
+0

Привет Martijn, Где выходит из этого self._locator? Это как раз и проблема. –

+0

@SteveWhite: базовый класс 'xml.sax.handler.ContentHandler' устанавливает' self._locator', когда 'setDocumentLocator()' вызывается парсером. Конечно, вы также можете реализовать свой * собственный метод 'handler.setDocumentLocator (локатор)', но почему у вас есть собака и кора? –

+0

Martijn, эта собака-собака зарегистрирована где? И как мне получить доступ к этому члену из подкласса MyContentHandler? Я пробовал, и, конечно, он говорит AttributeError: экземпляр MyContentHandler не имеет атрибута '_locator' –

Смежные вопросы