【问题标题】:How do I modify an XML element based on XML attribute content in Perl using XML::Simple?如何使用 XML::Simple 根据 Perl 中的 XML 属性内容修改 XML 元素?
【发布时间】:2021-10-18 10:33:36
【问题描述】:

我有以下格式的 XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<outer_tag>
        <inner_tag name="Name_0" template="Template_0">
        <string name="Name_1"><default>DEFAULT_NAME</default></string>
        <string name="Name_2"><default /></string>
        <string name="Name_3"><default></default></string>
        </inner_tag>
</outer_tag>

我需要修改内容为 "DEFAULT_NAME" 的元素,并且它的父标签属性需要是 "Name_1"。

所以,输出将是这样的:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<outer_tag>
        <inner_tag name="Name_0" template="Template_0">
        <string name="Name_1"><default>**NEW_NAME**</default></string>
        <string name="Name_2"><default /></string>
        <string name="Name_3"><default></default></string>
        </inner_tag>
</outer_tag>

我尝试了以下代码:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Simple;

my $xml_file = 'xml1.xml';

my $xml = XMLin(
    $xml_file,
    KeepRoot => 1,
    ForceArray => 1,
);

$xml->{outer_tag}->[0]->{string}->[0]->{default} = 'New_Name';

XMLout(
    $xml,
    KeepRoot => 1,
    NoAttr => 1,
    OutputFile => $xml_file,
);

但它在末尾添加了 New_Name1,而不是修改我需要替换的那个。我是 Perl 中的 XML 新手。

【问题讨论】:

  • 你试过什么?你有什么问题?请向我们展示您的代码。 (提示:最好的解决方案几乎肯定不使用 XML::Simple。)
  • 完全不相关,但可以节省一些输入:-&gt;optional between bracket subscripts,所以你可以改用它:$xml-&gt;{outer_tag}[0]{string}[0]{default} = 'New_Name'

标签: xml perl


【解决方案1】:

XML::Simple 自带following warning

请不要在新代码中使用此模块

因此,我建议使用另一个模块(例如,XML::TwigXML::LibXML)。如果您必须使用XML::Simple,请参阅我回答的结尾。

使用XML::Twig,您可以:

use XML::Twig;

my $xml_file = 'xml1.xml';
my $twig = XML::Twig->new();
$twig->parsefile($xml_file);

my $node = ($twig->get_xpath('/outer_tag/inner_tag/string[@name="Name_1"]/default'))[0];
$node->set_text('New_Name');

$twig->print;

如果需要,您可以缩进输出或打印到文件,请参阅the documentation

或者,使用XML::LibXML

use XML::LibXML;

my $xml_file = 'xml1.xml';
my $dom = XML::LibXML->load_xml(location => $xml_file);

my $node = ($dom->findnodes('/outer_tag/inner_tag/string[@name="Name_1"]/default/text()'))[0];
$node->setData('New_Name');

print $dom->toString

如果你坚持使用XML::Simple

您当前使用XML::Simple 的代码不起作用的原因是您假设$xml 的结构是什么,而您的假设是错误的。使用Data::DumperData::Printer,您可以轻松检查您的结构包含的内容,问题就很明显了:

use Data::Printer;

my $xml_file = 'xml1.xml';

my $xml = XMLin(
    $xml_file,
    KeepRoot => 1,
    ForceArray => 1,
    );

p $xml;

这将输出:

\ {
    outer_tag   [
        [0] {
            inner_tag   {
                Name_0   {
                    string     {
                        Name_1   {
                            default   [
                                [0] "DEFAULT_NAME"
                            ]
                        },
....

因此很明显你应该这样做

$xml->{outer_tag}->[0]->{inner_tag}->{Name_0}->{string}->{Name_1}->{default} = 'New_Name';

而不是

$xml->{outer_tag}->[0]->{string}->[0]->{default} = 'New_Name';

话虽如此,请使用XML::TwigXML::LibXML

【讨论】:

  • 我尝试了 XML::LibXML 并得到以下错误:Can't call method "setData" on an undefined value at test_xml.pl line 7. 我错过了什么吗?
  • @sbk 当我使用您提供的输入运行它时它可以工作。我的猜测是您的输入文件不是您在问题中粘贴的文件。如果不知道您的数据的实际结构,我无法帮助您。
  • 看起来是环境问题。感谢您的解决方案。它不会替换现有文件吗?
  • @sbk 我提供的 sn-ps 不会替换现有文件,但您可以open 并打印到它。例如,open my $FH, '&gt;', $xml_file,那么对于XML::LibXML,一个简单的print $FH $dom-&gt;toString 就可以了。对于XML::Twig,您可以使用$xml-&gt;print($FH)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多