使用XML :: LibXML和Perl查找/替换值

时间:2018-07-26 12:39:34

标签: perl xml-libxml

我在这里阅读了其他一些主题,但这不适合我的示例。 我有一个XML文件,我想在其中查找和替换特定值。

我的脚本正在处理预期的结果,但是这很丑陋,因为我没有使用模块中的所有资源。 我已经尝试过$node->setData($content);$dom->toFile($filename),但没有成功。

问题: 目的是从Media (XML示例的第29行第20列)中找到id,以便使用一种无​​需打开/关闭文件的更好的方式将其替换为$mediaIdFrom

以下是脚本:

#!/usr/bin/perl
use strict;
use warnings 'all';
use autodie;
use feature 'say';
use XML::LibXML;

my $mediaIdFrom = "MEDIAID_TEST";
my $VodItemIdFrom = "VODITEM_ID_TEST";

my $filename = 'sample.xml';
my $out_filename = $filename . ".new";

my $dom = XML::LibXML -> load_xml(location => $filename);

my $mediaId = join '', map { $_->{id}; } $dom->findnodes('/ScheduleProvider/Episode/Media');
my $vodItemId = join '', map { $_->{id}; } $dom->findnodes('/ScheduleProvider/VodItem');

if (-e $filename) {
        open(IN, "<", $filename);
        open(OUT, ">", $out_filename);
        while (<IN>) {
                chomp;
                $_ =~ s/\"$mediaId\"/\"$mediaIdFrom\"/g if /$mediaId/;
                $_ =~ s/\"$vodItemId\"/\"$VodItemIdFrom\"/g if /$vodItemId/;
                say $_;
                say OUT $_;
        }
        close(IN);
        close(OUT);
}

此处是示例:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ScheduleProvider id="TST" name="SHOP" scheduleDate="2018-07-05T23:05:45Z">
    <Product action="override" endPurchase="2018-08-04T21:59:00Z" endValidity="2018-09-05T03:31:00Z" id="TSTP937279650001" regions="Country" rentalDuration="2611920" startPurchase="2018-07-05T16:27:00Z" startValidity="2018-07-05T16:27:00Z" type="single">
        <Price currency="EUR" startPurchase="2018-07-05T16:27:00Z" endPurchase="2018-08-04T21:59:00Z">0.00</Price>
        <EpgDescription locale="fr_FR">
            <EpgElement key="Title">NO TITLE</EpgElement>
        </EpgDescription>
    </Product>
    <Series id="TST903350550001" action="override" title="Seasons - S1">
        <EpgDescription locale="fr_FR">
            <EpgElement key="Title">Seasons - S1</EpgElement>
            <EpgElement key="Synopsis">Smart</EpgElement>
            <EpgElement key="ShortTitle">Seasons - S1</EpgElement>
        </EpgDescription>
        <EpgDescription>
            <EpgElement key="Aspect">16:9</EpgElement>
            <EpgElement key="PromoImage">TST_ANT_1192194.jpg</EpgElement>
        </EpgDescription>
    </Series>
    <Episode action="override" duration="1080" id="TST937279650001" title="Épisode 9" number="9" episodeList="9" seriesRef="TST903350550001">
        <EpgDescription locale="fr_FR">
            <EpgElement key="Title">Épisode 9</EpgElement>
            <EpgElement key="Synopsis">George</EpgElement>
        </EpgDescription>
        <EpgDescription>
            <EpgElement key="Aspect">16:9</EpgElement>
            <EpgElement key="PromoImage">TST_ANT_1192194.jpg</EpgElement>
        </EpgDescription>
        <Media id="TSTM937279650001" fileName="TSTM937279650001.ts" frameDuration="27000" fileSize="477380316"/>
    </Episode>
    <VodItem action="override" contentRef="TST937279650001" id="TSTV937279650001" nodeRefs="TSTFRA1001" previewDate="2016-01-05T16:27:00Z" productRefs="TSTP937279650001" title="Épisode 9" broadcasterId="TST">
        <EpgDescription>
            <EpgElement key="Studio">Replay</EpgElement>
        </EpgDescription>
        <EpgDescription locale="fr_FR">
            <EpgElement key="Title">Épisode 9</EpgElement>
            <EpgElement key="Synopsis">George</EpgElement>
        </EpgDescription>
        <Period start="2018-07-05T16:27:00Z" end="2018-08-04T21:59:00Z"/>
    </VodItem>
</ScheduleProvider>

2 个答案:

答案 0 :(得分:2)

这将做您想要的。您只需要在setAttributes上找到的元素上调用findnodes

use strict;
use warnings 'all';

use XML::LibXML;

my $filename         = 'sample.xml';
my $out_filename     = "$filename.new";
my $media_id_from    = 'MEDIAID_TEST';
my $vod_item_id_from = 'VODITEM_ID_TEST';

my $doc = XML::LibXML->load_xml(location => $filename);

my ($media) = $doc->findnodes('/ScheduleProvider/Episode/Media');
$media->setAttribute(id => $media_id_from);

my ($vod_item) = $doc->findnodes('/ScheduleProvider/VodItem');
$vod_item->setAttribute(id => $vod_item_id_from);

$doc->toFile($out_filename);

答案 1 :(得分:1)

通过在属性名称前加上@作为XPath的最后一个元素,可以获取属性节点,因此请更改

my $mediaId = join '', map { $_->{id}; } $dom->findnodes('/ScheduleProvider/Episode/Media');
my $vodItemId = join '', map { $_->{id}; } $dom->findnodes('/ScheduleProvider/VodItem');

foreach my $attribute ($dom->findnodes('/ScheduleProvider/Episode/Media/@id') {
    $attribute->setValue($mediaIdFrom);
}

foreach my $attribute ($dom->findnodes('/ScheduleProvider/VoidItem/@id') {
    $attribute->setValue($VodItemIdFrom);
}

这将遍历每个id属性节点,允许您使用setValue()方法更新属性。

然后,您应该能够在dom对象上使用toFile()方法来写出更新的版本,而不必重新打开输入文件