XML созданный из JSON неправильно вложен с помощью perl - PullRequest
1 голос
/ 25 февраля 2020

Я пытаюсь преобразовать данные JSON в XML, используя скрипт perl. Но JSON при преобразовании не имеет ожидаемых тегов. Ниже ввод, код, который я использовал, и вывод, который я получил

{"status": "Success",
 "output":
     {"product_artifacts":
         [
             {"variant_name": "test_var",
 "artifacts":
                  [
                      {"artifact_created": "10-25-19 15:52:02",
 "artifact_download_link": "http://abc:rt/ ",
 "artifact_digital_size": 123,
 "artifact_number": "123/234",
 "artifact_revision": "AB1"}
                  ]
              }
         ]
      },
 "message":
     []
 }

Выше Json при передаче в приведенный ниже скрипт Perl, не создается XML, как ожидалось: Perl Script:

#!/app/perl/5.16.2/LMWP3/bin/perl

use strict;
use warnings;

binmode STDOUT, ":utf8";
use utf8;



use JSON;
use XML::Simple;

# Read input file in json format
my $json = '
{"status": "Success",
 "output":
     {"product_artifacts":
         [
             {"variant_name": "test_var",
 "artifacts":
                  [
                      {"artifact_created": "10-25-19 15:52:02",
 "artifact_download_link": "http://abc:rt/ ",
 "artifact_digital_size": 123,
 "artifact_number": "123/234",
 "artifact_revision": "AB1"}
                  ]
              }
         ]
      },
 "message":
     []
 }';

# Convert JSON format to perl structures
my $data = decode_json($json);

# Output as XML
print "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
print XMLout($data);
print "\n";

Фактический результат:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<opt status="Success">
  <output>
    <product_artifacts variant_name="test_var">
      <artifacts artifact_created="10-25-19 15:52:02" artifact_digital_size="9293792" artifact_download_link="http://abc:rt " artifact_number="123/234" artifact_revision="AC" />
    </product_artifacts>
  </output>
</opt>

Ожидаемый результат:

<?xml version="1.0" encoding="UTF-8" ?>
<root>
  <status>Success</status>
  <output>
    <product_artifacts>
      <variant_name>test_var</variant_name>
      <artifacts>
        <artifact_created>10-25-19 15:52:02</artifact_created>
        <artifact_download_link>http://asd:rt </artifact_download_link>
        <artifact_digital_size>123</artifact_digital_size>
        <artifact_number>1234</artifact_number>
        <artifact_revision>AC</artifact_revision>
      </artifacts>
    </product_artifacts>
  </output>
  <message/>
</root>

Может ли кто-нибудь помочь, где я я иду не так

Ответы [ 3 ]

1 голос
/ 26 февраля 2020

Perl структуры данных напрямую не отображаются на XML. Например, хэш-ссылка в определенной позиции может быть представлена ​​атрибутами тега или вложенными тегами, которые сами могут иметь атрибуты, теги или текст. Таким образом, чтобы получить выходные данные, отформатированные так, как вы хотите, одним из способов является использование шаблонов для определения структуры, которую вы хотите, например, с помощью Mojo :: Template .

use strict;
use warnings;
use Mojo::Template;

my $tmpl = <<'TMPL';
<?xml version="1.0" encoding="UTF-8" ?>
<root>
  <status><%= $data->{status} %></status>
  <output>
    <product_artifacts>
    % foreach my $variant (@{$data->{output}{product_artifacts}}) {
      <variant_name><%= $variant->{variant_name} %></variant_name>
      <artifacts>
      % foreach my $artifact (@{$variant->{artifacts}}) {
        % foreach my $key (sort keys %$artifact) {
          <<%= $key %>><%= $artifact->{$key} %></<%= $key %>>
        % }
      % }
      </artifacts>
    % }
    </product_artifacts>
  </output>
  <message/>
</root>
TMPL

my $t = Mojo::Template->new(auto_escape => 1, vars => 1);
my $xml = $t->render($tmpl, {data => $data});

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


Другой подход заключается в использовании инструмента обхода XML для правильного построения XML, который является немного более утомительно, но означает, что вам не нужно писать теги вручную, Mojo :: DOM может быть использовано для этой цели.

use strict;
use warnings;
use Mojo::DOM;

my $dom = Mojo::DOM->new->xml(1)->parse('<?xml version="1.0" encoding="UTF-8" ?><root/>');

my $root = $dom->at('root');
$root->append_content($dom->new_tag('status', $data->{status}));
$root->append_content($dom->new_tag('output'));
my $output = $root->at('output');
$output->append_content($dom->new_tag('product_artifacts'));
my $product_artifacts = $output->at('product_artifacts');
foreach my $variant (@{$data->{output}{product_artifacts}}) {
  $product_artifacts->append_content($dom->new_tag('variant_name', $variant->{variant_name}));
  $product_artifacts->append_content($dom->new_tag('artifacts'));
  my $artifacts = $product_artifacts->at('artifacts');
  foreach my $artifact (@{$variant->{artifacts}}) {
    foreach my $key (sort keys %$artifact) {
      $artifacts->append_content($dom->new_tag($key, $artifact->{$key}));
    }
  }
}
$root->append_content($dom->new_tag('message', $data->{message}));

my $xml = $dom->to_string;

Оба эти примера приводят к XML как персонажи; он должен быть закодирован в UTF-8 при выводе в файл или другим способом.

0 голосов
/ 28 февраля 2020

Обратите внимание, что XML :: Simple устарела, и сам автор рекомендует использовать другие модули. Однако я не знаю, какой модуль можно использовать для простого выгрузки структур данных в XML (за исключением, может быть, XML :: Dumper, но у него совсем другая структура вывода) без "ручного" создания структуры данных.

Для желаемого формата вывода необходимо установить следующие параметры для функции XMLOut:

print XMLout($data,NoAttr => 1, RootName => 'root');

Однако при этом все равно останется тег «message», который является пустым массивом. и XML :: Simple, кажется, молча отбрасывает его (ууу!).

<root>
  <output>
    <product_artifacts>
      <artifacts>
        <artifact_created>10-25-19 15:52:02</artifact_created>
        <artifact_digital_size>123</artifact_digital_size>
        <artifact_download_link>http://abc:rt/ </artifact_download_link>
        <artifact_number>123/234</artifact_number>
        <artifact_revision>AB1</artifact_revision>
      </artifacts>
      <variant_name>test_var</variant_name>
    </product_artifacts>
  </output>
  <status>Success</status>
</root>

Необработанным обходным путем для этого было бы установить его в undef и установить для параметра SuppressEmpty значение undef, но это все равно не сгенерирует идентичный вывод, так как XML :: Simple не похоже, генерирует пустые теги.

См. https://ideone.com/kwqZzo для демонстрации и полного кода.

Кроме того, вы можете создать xml вручную, используя XML :: Writer , но это сильно зависит от данных. Вы можете попробовать рекурсивный подход, но он будет хрупким. Для точных json и выходных данных в вопросе вы можете использовать что-то вроде этого:

#!/app/perl/5.16.2/LMWP3/bin/perl

use strict;
use warnings;

binmode STDOUT, ":utf8";
use utf8;



use JSON;

use XML::Writer;

# Read input file in json format
my $json = qq(
{
  "status": "Success",
  "output": {
    "product_artifacts": [
      {
        "variant_name": "test_var",
        "artifacts": [
          {
            "artifact_created": "10-25-19 15:52:02",
            "artifact_download_link": "http://abc:rt/ ",
            "artifact_digital_size": 123,
            "artifact_number": "123/234",
            "artifact_revision": "AB1"
          }
        ]
      }
    ]
  },
  "message": []
}
);
my $data = decode_json($json);
my $writer = XML::Writer->new( OUTPUT => 'self',DATA_MODE => 1, DATA_INDENT => 4);

$writer->xmlDecl("UTF-8");
$writer->startTag('root');
    $writer->dataElement(status => $data->{status});
    $writer->startTag('output');
       for my $p (@{$data->{output}{product_artifacts}}) {
           $writer->startTag('product_artifacts');
           $writer->dataElement($_ => $p->{$_}) for qw(variant_name);
           for my $a (@{$p->{artifacts}}) {
               $writer->startTag('artifacts');
               $writer->dataElement($_ => $a->{$_}) for qw(artifact_created
                                                       artifact_download_link
                                                       artifact_digital_size
                                                       artifact_number
                                                       artifact_revision);
               $writer->endTag('artifacts');
           }
           $writer->endTag('product_artifacts');
       }
    $writer->endTag('output');
    $writer->emptyTag('message');
$writer->endTag('root');

print $writer->to_string();
print "\n";
0 голосов
/ 25 февраля 2020

На данный момент решения не предлагалось.

Позвольте мне предложить одно из возможных простых решений без использования каких-либо perl модулей.

use strict;
use warnings;
use feature 'say';

use JSON;

binmode STDOUT, ":utf8";
use utf8;

my $json = '
{"status": "Success",
 "output":
    { "product_artifacts":
         [
            {
                "variant_name": "test_var",
                "artifacts":
                    [
                        {
                            "artifact_created": "10-25-19 15:52:02",
                            "artifact_download_link": "http://abc:rt/ ",
                            "artifact_digital_size": 123,
                            "artifact_number": "123/234",
                            "artifact_revision": "AB1"
                        }
                    ]
            }
         ]
    },
    "message":[]
 }';

# Convert JSON format to perl structures
my $data = decode_json($json);

say json2xml($data);

sub json2xml {
    my $data  = shift;

    my $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";

    $xml .= "<root>\n";
    $xml .= j2x($data,1);
    $xml .= "</root>\n";

    return $xml;
}

sub j2x {
    my $json  = shift;
    my $depth = shift;

    my $xml;
    my $indent = 2;
    my $space = ' ' x ($depth*$indent);

    while( my($k,$v) = each %{$json} ) {
        if( ref $v eq 'HASH' ) {
            $xml .= $space . "<$k>\n";
            $xml .= j2x($v,$depth+1);
            $xml .= $space . "</$k>\n";
        } elsif ( ref $v eq 'ARRAY' ) {
            $xml .= $space . "<$k>\n";
            foreach my $e (@{$v}) {
                $xml .= j2x($e,$depth+1);
            }
            $xml .= $space . "</$k>\n";
        } else {
            $xml .= $space . "<$k>$v</$k>\n";
        }
    }

    return $xml;
}

Вывод немного отличается от желаемого, так как код не учитывает пустой * Элемент 1013 * (пустой массив JSON в данном конкретном случае)

<?xml version="1.0" encoding="UTF-8" ?>
<root>
  <status>Success</status>
  <output>
    <product_artifacts>
      <variant_name>test_var</variant_name>
      <artifacts>
        <artifact_number>123/234</artifact_number>
        <artifact_created>10-25-19 15:52:02</artifact_created>
        <artifact_revision>AB1</artifact_revision>
        <artifact_digital_size>123</artifact_digital_size>
        <artifact_download_link>http://abc:rt/ </artifact_download_link>
      </artifacts>
    </product_artifacts>
  </output>
  <message>
  </message>
</root>

ПРИМЕЧАНИЕ. В посте OP JSON и требуемый вывод не совпадают, поскольку по этой причине полученный вывод является представлением JSON данные приведены в сообщении

...