Search code examples
perlxml-libxml

Find / Replace values using XML::LibXML with Perl


I've read some others topics here but this doesn't fit to my sample. I have an XML file where I want to find and replace specific values.

My script is working with expecting result, but it's quite ugly because I don't use all reassources from the module. I've tried with $node->setData($content); and $dom->toFile($filename) but without successful.

Question: The goal is to find the id from the Media (line 29 column 20 from the XML sample) in order to replace it by $mediaIdFrom with a better way using the module without open/close files.

Here the script:

#!/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);
}

Here the sample:

<?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>

Solution

  • This will do what you want. You just needed to call setAttributes on the elements that you had located with 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);