Как я могу заказать теги в выводе XML :: Simple? - PullRequest
3 голосов
/ 09 сентября 2009

Вот мой сценарий:

Мне нужно сгенерировать XML через Perl, в котором схема заполнена тегами <xs:sequence> (т. Е. Теги ДОЛЖНЫ появляться в порядке). У меня нет контроля над схемой (третьей стороной), и когда мы добавляем новые модули CPAN (у нас нет хорошего способа их распространения среди клиентов и т. Д.), Возникает так много проблем, что нам в принципе запрещено добавление чего-либо нового (например, XML::Writer).

Модули XML в моем распоряжении: XML::Parser, XML::Simple, XML::XPath.

Мне очень нравится, как в XML::Simple вы создаете структуру данных hashref w / hash / arary refs, а затем просто выплевываете XML.

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

Такое чувство, что я вроде SOL, но определенно буду благодарен за любые трюки / идеи, которые кто-то может иметь. Спасибо.

Ответы [ 5 ]

11 голосов
/ 09 сентября 2009

Большую часть времени конфигурация может быть выполнена только с опциями, которые предоставляет XML::Simple. Обычно он автоматически сворачивает данные в простую структуру данных, которую можно логически воспроизвести; он отлично подходит для форматов хранения данных, но не настолько силен, когда дело доходит до соответствия формату документа. К счастью, несмотря на то, что он «простой», он невероятно мощный.

Для управления порядком вывода элементов у вас есть несколько вариантов. Вы можете использовать массивы, которые гарантируют порядок данных. Однако, похоже, вам нужен определенный порядок значений для одного тега.

Сортировка ключей также является автоматической функцией. Пока ключи у вас в алфавитном порядке, они будут гарантированно отсортированы в указанном порядке.

Но много раз, особенно с очень конкретными схемами, это не сработает. К счастью, XML :: Simple по-прежнему поддерживает способ его настройки: вы должны использовать интерфейс OO и переопределить метод sorted_keys. Вот пример:

use strict;
use warnings;
use XML::Simple;
use Data::Dumper;

package MyXMLSimple;      # my XML::Simple subclass 
use base 'XML::Simple';

# Overriding the method here
sub sorted_keys
{
   my ($self, $name, $hashref) = @_;
   if ($name eq 'supertag')   # only this tag I care about the order;
   {
      return ('tag1', 'tag3','tag4','tag10'); # so I specify exactly the right order.
   }
   return $self->SUPER::sorted_keys($name, $hashref); # for the rest, I don't care!
}

package main; # back to main code

my $xmlParser = MyXMLSimple->new(      # Create the parser, with options:
                  KeepRoot => 1,       # gives us our root element always.
                  ForceContent => 1,   # ensures that content stays special
               );

my $structure = { 
   supertag => { 
      tag1  => { content => 'value 1' },
      tag10 => { content => 'value 2' },
      tag3  => { content => 'value 3' },
      tag4  => { content => 'value 4' },
   },
};

my $xml = $xmlParser->XMLout($structure);
print "The xml generated is:\n$xml\n";
print "The read in structure then is:\n" . $xmlParser->XMLin($xml) . "\n";

Это даст нам:

The xml generated is:
<supertag>
  <tag1>value 1</tag1>
  <tag3>value 3</tag3>
  <tag4>value 4</tag4>
  <tag10>value 2</tag10>
</supertag>

The read in structure then is:
$VAR1 = {
          'supertag' => {
                          'tag10' => {
                                       'content' => 'value 2'
                                     },
                          'tag3' => {
                                      'content' => 'value 3'
                                    },
                          'tag1' => {
                                      'content' => 'value 1'
                                    },
                          'tag4' => {
                                      'content' => 'value 4'
                                    }
                        }
        };

Проверьте страницу XML :: Simple на CPAN.

1 голос
/ 09 сентября 2009

Вы все еще можете использовать `XML :: Simple 'и предоставить метод подключения , который выполняет сортировку. Я знаю, это уродливо, и вы бы предпочли что-то, что не производит дополнительного кода.

0 голосов
/ 07 января 2014

Я нашел проблему с решением @ mraq. Я использовал его и внезапно получил атрибуты в «супертаге», которые мне не нужны и которые были пусты. Причина в том, что у меня не было tag3 и tag4, поэтому оба стали пустыми атрибутами супертага.

Небольшое исправление сделает:

package MyXMLSimple;      # my XML::Simple subclass 
use base 'XML::Simple';

# Overriding the method here
sub sorted_keys
{
   my ($self, $name, $hashref) = @_;
   if ($name eq 'dsedib2b')   # only this tag I care about the order;
   {
      my @ordered = qw(
          tag1
          tag3
          tag4
          tag10
      );
      my %ordered_hash = map {$_ => 1} @ordered;

      #set ordered tags in front of others
      return grep {exists $hashref->{$_}} @ordered, grep {not $ordered_hash{$_}} $self->SUPER::sorted_keys($name, $hashref);
   }
   return $self->SUPER::sorted_keys($name, $hashref); # for the rest, I don't care!
}

Так что просто заменив

return @ordered, grep {not $ordered_hash{$_}} $self->SUPER::sorted_keys($name, $hashref);

с

return grep {exists $hashref->{$_}} @ordered, grep {not $ordered_hash{$_}} $self->SUPER::sorted_keys($name, $hashref);

исправляет вещь.

0 голосов
/ 17 августа 2012

Адресация комментария @Konerak Я предлагаю небольшое улучшение решения @ robert-p. Сохраняет не отсортированные теги от исчезновения.

#!/usr/bin/perl -w
use strict;
use warnings;
use XML::Simple;
use Data::Dumper;

package MyXMLSimple;      # my XML::Simple subclass 
use base 'XML::Simple';

# Overriding the method here
sub sorted_keys
{
   my ($self, $name, $hashref) = @_;
   if ($name eq 'supertag')   # only this tag I care about the order;
   {
      my @ordered = ('tag1', 'tag3','tag4','tag10');
      my %ordered_hash = map {$_ => 1} @ordered;

      #set ordered tags in front of others
      return @ordered, grep {not $ordered_hash{$_}} $self->SUPER::sorted_keys($name, $hashref);
   }
   return $self->SUPER::sorted_keys($name, $hashref); # for the rest, I don't care!
}

package main; # back to main code

my $xmlParser = MyXMLSimple->new(      # Create the parser, with options:
                  KeepRoot => 1,       # gives us our root element always.
                  ForceContent => 1,   # ensures that content stays special
               );

my $structure = { 
   supertag => { 
      tag1  => { content => 'value 1' },
      tag10 => { content => 'value 2' },
      tag3  => { content => 'value 3' },
      tag4  => { content => 'value 4' },
      tag90  => { content => 'value 90' },
      tag91  => { content => 'value 91' },
   },
};

my $xml = $xmlParser->XMLout($structure);
my $xml2 = $xmlParser->XMLin($xml);  
print "The xml generated is:\n$xml\n";
print "The read in structure then is:\n" . Dumper($xml2) . "\n";

выход

The xml generated is:
<supertag>
  <tag1>value 1</tag1>
  <tag3>value 3</tag3>
  <tag4>value 4</tag4>
  <tag10>value 2</tag10>
  <tag90>value 90</tag90>
  <tag91>value 91</tag91>
</supertag>

The read in structure then is:
$VAR1 = {
          'supertag' => {
                        'tag10' => {
                                   'content' => 'value 2'
                                 },
                        'tag3' => {
                                  'content' => 'value 3'
                                },
                        'tag1' => {
                                  'content' => 'value 1'
                                },
                        'tag91' => {
                                   'content' => 'value 91'
                                 },
                        'tag90' => {
                                   'content' => 'value 90'
                                 },
                        'tag4' => {
                                  'content' => 'value 4'
                                }
                      }
        };
0 голосов
/ 09 сентября 2009

Этот код выдаст результат, который вы просили в комментарии:

use strict;
use warnings;
use XML::Simple;

my $structure = { 'supertag' => [
      'value 1',
      'value 2',
      'value 3',
      'value 4',
   ],
};

my $xml = XMLout($structure, GroupTags => { supertag => 'tag'});

print "The xml generated is:\n";
print $xml;
print "\n";

Генерирует:

The xml generated is:
<opt>
  <supertag>
    <tag>value 1</tag>
    <tag>value 2</tag>
    <tag>value 3</tag>
    <tag>value 4</tag>
  </supertag>
</opt>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...