Манипулирование правильно сформированным xml (на любом языке, работающем под Linux)

1

У меня есть хорошо сформированный xml (открытые теги закрыты и т.д.), но там нет dtd, пространства имен не всегда корректны и существуют случайные объекты.

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

<foo>
  <bar>      hi </bar>
  <!-- ... -->
  <math><sometag><another>bar</another></sometag></math>
  <!-- ... -->
</foo>

Я хочу изменить это на

<foo>
  <bar>      hi </bar>
  <!-- ... -->
  <m:math><m:sometag><m:another>bar</m:another></m:sometag></m:math>
  <!-- ... -->
</foo>

Я посмотрел на элемент Python elementtree, но в соответствии с diveintopython ему не понравится тот факт, что он не проверяет xml? Кроме того, важно, чтобы ничто не изменялось, кроме префикса m:.

Так как я пишу кучу shell-скриптов для исправления файлов, мне действительно не нужен язык, хотя моим текущим оружием выбора является Python.

Разъяснения:

  • xml действительно проходит при выполнении xmllint на нем
  • Я действительно хочу xml-решение, потому что синтаксический анализ xml с использованием регулярных выражений - путь к flakey
  • Я не знаю имена тегов, которые могут находиться между <math> и </math>
  • В документ не должны быть внесены изменения, кроме префикса вышеупомянутых тегов m:
  • 0
    +1 "парсинг XML с помощью регулярных выражений - это путь к ошибкам"
  • 0
    В конце концов я сопоставил <math> ... </ math> с регулярным выражением (потому что тогда я могу использовать эту замену в той точке процесса, где xml еще не гарантированно проверен): stackoverflow.com/questions / 5409161 /… . Я сначала попробовал с Beautifulsoup, но для xml это кажется более безопасным, и это экономит место, но просто теряет комментарии. Всем спасибо! Я многому научился :)
Теги:

5 ответов

2
Лучший ответ

В Ruby, используя Nokogiri для массажа XML:

xml = <<EOT
<foo>
  <bar>      hi </bar>
  <!-- ... -->
  <math><sometag><another>bar</another></sometag></math>
  <!-- ... -->
</foo>
EOT

NAMESPACE = %w[m http://host.com/m]

require 'nokogiri'
doc = Nokogiri::XML::DocumentFragment.parse(xml)

ns = doc.at('foo').add_namespace_definition(*NAMESPACE)

doc.xpath('foo/math | foo/math//*').each { |n| n.namespace = ns }

puts doc.to_xml 

Результат выглядит следующим образом:

>> <foo xmlns:m="http://host.com/m">
>>   <bar>      hi </bar>
>>   <!-- ... -->
>>   <m:math><m:sometag><m:another>bar</m:another></m:sometag></m:math>
>>   <!-- ... -->
>> </foo>

Если пространство имен не может быть добавлено в <foo>, вы можете напрямую пропустить имена тегов, не входя в пространства имен:

xml = <<EOT
<foo>
  <bar>      hi </bar>
  <!-- ... -->
  <math><sometag><another>bar</another></sometag></math>
  <!-- ... -->
</foo>
EOT

NAMESPACE = %w[m http://host.com/m]

require 'nokogiri'
doc = Nokogiri::XML::DocumentFragment.parse(xml)

doc.xpath('foo/math | foo/math//*').each { |n| n.name = "m:" << n.name }

puts doc.to_xml

# >> <foo>
# >>   <bar>      hi </bar>
# >>   <!-- ... -->
# >>   <m:math><m:sometag><m:another>bar</m:another></m:sometag></m:math>
# >>   <!-- ... -->
# >> </foo>
6

В Perl вы можете использовать XML:: Twig, например:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

XML::Twig->new( twig_roots => { math => \&add_prefix },
                twig_print_outside_roots => 1,
              )
         ->parse( \*DATA);

sub add_prefix
  { my( $t, $math)= @_;
    foreach my $m ( $math, $math->descendants( '#ELT'))
      { $m->set_tag( "m:" . $m->tag); }
    $t->flush;
  }

__DATA__
<foo>
  <bar>      hi </bar>
  <!-- ... -->
  <math><sometag><another>bar</another></sometag></math>
  <!-- ... -->
</foo>
4

Один слой в Perl ok?

$ perl -lne'm!<math>.*</math>! and s!<(/)?([^>]+)>!<$1m:$2>!gm;print' 5351382.txt
<foo>
  <bar>      hi </bar>
  <!-- ... -->
  <m:math><m:sometag><m:another>bar</m:another></m:sometag></m:math>
  <!-- ... -->
</foo>

Вы не должны разбираться с XML таким образом... но если выше для вас достаточно...;)

  • 0
    Хех, я всегда ценю хороший oneliner;) Но я бы предпочел использовать метод парсинга XML, так как я знаю, что теги хорошо сбалансированы и тому подобное (я использовал xmllint, чтобы проверить, хорошо ли сбалансирован xml). Такие методы манипулирования текстом хитры, например, ваше решение заменяет <!-- --> на <m:!-- --> . Поэтому я бы предпочел сделать это с помощью библиотеки xml.
  • 0
    Я действительно заинтригован этим m!! and s!!! строительство. Не могли бы вы объяснить, как это работает, или указать на сайт, который это объясняет? Я сделал довольно много perl oneliners, но не знал эту конструкцию.
Показать ещё 2 комментария
1

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

Класс BeautifulSoup полон эвристик, основанных на веб-браузере, для того, чтобы предсказать намерение авторов HTML. Но у XML нет фиксированного набора тегов, поэтому эти эвристики не применяются. Поэтому BeautifulSoup не очень хорошо выполняет XML.

Это может быть не идеально, но, вероятно, лучше работает на неопределенный или недопустимый XML, чем это делает строгий парсер. Еще один момент в его пользу заключается в том, что он дает вам Unicode, dammit.

  • 0
    Но сохраняет ли это оригинальный макет с интервалом и т. Д.? Я нахожусь в середине конвейера, и не имею большого контроля над тем, что происходит с файлом после этого, и при этом я не знаю, что именно происходит. Поэтому мне нужно ошибиться на безопасной стороне и использовать только операции, которые не изменяют интервал / комментарии / отступы или что-либо еще.
  • 0
    Нет, я не думаю, что это делает какие-либо попытки сохранить пробелы ... извините.
Показать ещё 1 комментарий
1

Лучше всего, скорее всего, найти невращающийся процессор XSLT и передать ему что-то вроде: <xsl:template match="math"> <m:math> <xsl:apply-templates select="@*|node()"/> </m:math> </xsl:template>

  • 0
    Но это не добавит префикс m: к тегам внутри тега <math>, верно? И я не знаю названий тегов, которые могут встречаться между <math> и </math> .
  • 0
    О, да, это именно то, что он делает. И не имеет значения, что внутри них, об этом позаботится предложение select="@*|node()" .

Ещё вопросы

Сообщество Overcoder
Наверх
Меню