I have a nested XML tags and need to have the value of ExternalId in Product XML to a ProductPageURL tag using XML Twig
<Products>
<Product>
<ExternalId>317851</ExternalId>
<ProductPageUrl></ProductPageUrl>
</Product>
<Product>
<ExternalId>316232</ExternalId>
<ProductPageUrl></ProductPageUrl>
</Product>
<Product>
<ExternalId>13472</ExternalId>
<ProductPageUrl></ProductPageUrl>
</Product>
</Products>
Expected result:
<Products>
<Product>
<ExternalId>PF317851</ExternalId>
<ProductPageUrl>317851</ProductPageUrl>
</Product>
<Product>
<ExternalId>PF316232</ExternalId>
<ProductPageUrl>316232</ProductPageUrl>
</Product>
<Product>
<ExternalId>PF13472</ExternalId>
<ProductPageUrl>13472</ProductPageUrl>
</Product>
</Products>
I have using the below logic using XML twig
my $twig = XML::Twig->new(
twig_handlers => {
'Product/ExternalId' => sub {
$_->prefix( 'PF' );
},
'Product/ProductPageUrl' => sub {
$_->set_text($_->get('Product/ExternalId'));
},
},
pretty_print => 'indented',
keep_encoding => 1,
)->parsefile($xml_path_filename )->print_to_file($xml_path_filename);
Could you let me know how to make the code easier, I am not able to acheive the expected result for now
There are 2 problems in your initial code: first I don't think get
is an XML::Twig::Elt method. Then you first prefix the ExternalId
text, then (once prefixed) try to use it to update ProductPageUrl
. That won't work. In this case I think you're better off having a single handler, for the Product
tag, in which you get the id data, then update both sub-elements.
Here a solution, written as a test so it's easier to update if your output changes:
#!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 1;
use XML::Twig;
# in and expected are in the DATA section, separated by 2 \n
my( $in, $expected)= do { local $/="\n\n"; <DATA>};
my $t= XML::Twig->new( twig_handlers => { Product => \&update_product },
keep_spaces => 1,
)
->parse( $in);
is( $t->sprint, $expected, "one test to rule them all");
sub update_product
{ my( $t, $product)= @_;
my $id= $product->field( 'ExternalId');
$product->first_child( 'ExternalId')->prefix( 'PF');
$product->first_child( 'ProductPageUrl')->set_text( $id);
}
__DATA__
<Products>
<Product>
<ExternalId>317851</ExternalId>
<ProductPageUrl></ProductPageUrl>
</Product>
<Product>
<ExternalId>316232</ExternalId>
<ProductPageUrl></ProductPageUrl>
</Product>
<Product>
<ExternalId>13472</ExternalId>
<ProductPageUrl></ProductPageUrl>
</Product>
</Products>
<Products>
<Product>
<ExternalId>PF317851</ExternalId>
<ProductPageUrl>317851</ProductPageUrl>
</Product>
<Product>
<ExternalId>PF316232</ExternalId>
<ProductPageUrl>316232</ProductPageUrl>
</Product>
<Product>
<ExternalId>PF13472</ExternalId>
<ProductPageUrl>13472</ProductPageUrl>
</Product>
</Products>
Here is one way:
use warnings;
use strict;
use XML::Twig;
my $twig = XML::Twig->new(
twig_handlers => {
Product => \&prod,
},
pretty_print => 'indented',
keep_encoding => 1,
);
$twig->parse(q(
<Products>
<Product>
<ExternalId>317851</ExternalId>
<ProductPageUrl></ProductPageUrl>
</Product>
<Product>
<ExternalId>316232</ExternalId>
<ProductPageUrl></ProductPageUrl>
</Product>
<Product>
<ExternalId>13472</ExternalId>
<ProductPageUrl></ProductPageUrl>
</Product>
</Products>
));
$twig->print();
sub prod {
my ($t, $prod) = @_;
my $extid = $prod->first_child('ExternalId');
my $id = $extid->text();
$extid->prefix('PF');
my $url = $prod->first_child('ProductPageUrl');
$url->set_text($id);
}
output
<Products>
<Product>
<ExternalId>PF317851</ExternalId>
<ProductPageUrl>317851</ProductPageUrl>
</Product>
<Product>
<ExternalId>PF316232</ExternalId>
<ProductPageUrl>316232</ProductPageUrl>
</Product>
<Product>
<ExternalId>PF13472</ExternalId>
<ProductPageUrl>13472</ProductPageUrl>
</Product>
</Products>