Разобрать несколько объявлений XML в одном файле с помощью lxml.etree.iterparse - PullRequest
4 голосов
/ 13 апреля 2011

Мне нужно проанализировать файл, который содержит различные файлы XML, то есть .. и так далее. При использовании etree.iterparse я получаю следующую (правильную) ошибку:

lxml.etree.XMLSyntaxError: XML declaration allowed only at the start of the document

Теперь я могу предварительно обработать входной файл и создать для каждого содержащегося XML-файла отдельный файл. Это может быть самым простым решением. Но мне интересно, существует ли правильное решение для этой «проблемы».

Спасибо!

Ответы [ 2 ]

3 голосов
/ 13 апреля 2011

Предоставленные вами примеры данных указывают на одну проблему, а вопрос и исключение, которые вы предоставили, указывают на другую. У вас есть несколько документов XML, соединенных вместе, каждый со своим собственным объявлением XML, или у вас есть фрагмент XML с несколькими элементами верхнего уровня?

Если это первое, то решение будет включать разбиение входного потока на несколько потоков и анализ каждого из них по отдельности. Это не обязательно означает, как предполагает один комментарий, реализацию синтаксического анализатора XML. Вы можете искать в строке объявления XML, не анализируя в ней ничего другого, при условии, что ваши входные данные не содержат разделов CDATA, которые содержат неэкранированные объявления XML. Вы можете написать файлоподобный объект, который возвращает символы из базового потока до тех пор, пока он не достигнет объявления XML, а затем обернуть его в функцию генератора, которая продолжает возвращать потоки до достижения EOF. Это не тривиально, но и не очень сложно.

Если у вас есть фрагмент XML с несколькими элементами верхнего уровня, вы можете просто обернуть их элементом XML и проанализировать все это.

Конечно, как и в случае большинства проблем, связанных с неправильным вводом XML, простейшим решением может быть просто исправить то, что вызывает неправильный ввод.

0 голосов
/ 13 мая 2011

Я использовал регулярные выражения для решения этой проблемы. Предположим, что данные - это строка, содержащая несколько ваших XML-документов, а этот дескриптор - функция, которая будет что-то делать с каждым документом. После выполнения этого цикла данные будут пустыми или будут содержать неполный документ XML, а функция дескриптора будет вызываться ноль или более раз.

while True:
  match = re.match (r'''
        \s*                 # ignore leading whitespace
        (                   # start first group
          <(?P<TAG>\S+).*?> # opening tag (with optional attributes)
            .*?             # stuff in the middle
          </(?P=TAG)>       # closing tag
        )                   # end of first xml document
        (?P<REM>.*)         # anything else
      ''',
    data, re.DOTALL | re.VERBOSE)
  if not match:
    break
  document = match.group (1)
  handle (document)
  data = match.group ('REM')
...