in reply to XML Search and Replace

I don't really understand what you are trying to do, but I think you have a problem with how you use the arguments passed to the handler (search). The handler receives 2 arguments: the twig ($orig in this case) and the current element ($search). So really you can't write $search-$gt;prev_elt( 'name' )->text eq $search, search is an XML::Twig::Elt object, not a string. Then replace is a method on an element, not on a twig, so you probably don't want to write $orig->replace( $value );.

Would this work? (I can't test it without the actual XML data) It should work provided the text of the name element before the $search element is $search, which seems a bit odd to me.

#!/bin/perl -w use strict; use XML::Twig; my( $main_file, $search, $value )= @ARGV; # get the info we need by loading the update file #my $t_upd= new XML::Twig(); #$t_upd->parsefile( $upd_file); #my $upd_badge_id = $t_upd->root->next_elt( 'badge_id')->text; #my $upd_chore = $t_upd->root->next_elt( 'jobs'); # Process the main file my $orig = new XML::Twig( TwigHandlers => { $search => \&search, }, PrettyPrint => 'indented', ); $orig->parsefile( $main_file ); $orig->flush; # don't forget or the last closing tags won't +be printed sub search { my( $orig, $search )= @_; print "hrmmm\n"; my $search_tag= $search->tag; # just replace jobs if the previous badge_id is the right one if( $search->prev_elt( 'name' )->text eq $search_tag ) { print "hrmmm\n"; $search->set_text( $value ); } $orig->flush; # print and flush memory so only one job is in th +ere at once }

Replies are listed 'Best First'.
Re: Re: XML Search and Replace
by coreolyn (Parson) on Jun 11, 2002 at 17:01 UTC

    I'll test the above after I post this sample Data. The problem here is this sample data while fine for 'an' example is not indicitive of each XML that may need to be processed. This script is part of a familiy of deployment scripts that deploy applications to various servers. Each deployment might reference a different set of servers and the XML tree may be completely different.

    This had previously been done via a name=value pairs in a user supplied configuration file and a script would go through a flat(property)file, find the name and substitue the supplied value. Then someone decided it would be better if the property files that were being updated would be in XML.

    Here a sample file:

    <ImageQuery547> <root> <TraceNumQuery> <Path>http://localhost:8080/image/ImageVendorServlet?</Pat +h> <TraceNum>trace</TraceNum> <Date>dt</Date> <Face>fb </Face></TraceNumQuery> <CheckNumQuery> <Path>http://666.666.210.72/wetest/we.dll?</Path> <Account>acct</Account> <Amount>amt</Amount> <CheckNum>sn</CheckNum> <Date>dt</Date> <Face>fb</Face> <Ping>ping</Ping> </CheckNumQuery></root> </ImageQuery547>

    Typically the values of the Paths (Servers) would be changed on each deployement. This creates the problem of identifying a <TraceNumQuery><Path> from a <CheckNumQuery><Path> and inserting the correct values.

    In this case what I would like to is pick up (from a flat file config file ) $filename $element (this could be in the form of "TraceNumQuery::Path" and $value ("http://foo.bar:8080/baz"), and substitue it for the value currently at TraceNumQuery::Path" (http://localhost:8080/image/ImageVendorServlet?)

    coreolyn (Should've supplied this right away doh!)

      So here is how I would do it: you only need to update this one tag (possibly many times in the file), so I would use twig_roots and twig_print_outside_roots here: you just go through the file, outputting it as-is unless you find the path, in which case you use a handler to change the content of the element. This will give you the minimum memory footprint (and it's pretty simple too!).

      Call this as update file.xml TraceNumQuery/Path http://foo.bar:8080/baz

      #!/usr/bin/perl -w use strict; use XML::Twig; my $USAGE= "$0 <file> <path_to_update> <value>"; die $USAGE unless( @ARGV == 3); my( $file, $path, $value)= @ARGV; # $_ is set to the current element in the handler # you could also delete the element after printing it # for even less memory usage my $twig= XML::Twig->new( twig_roots => { $path => sub { $_->set_text( + $value); $_->print; } }, twig_print_outside_roots => 1, ); $twig->parsefile( $file);

        Sweetness! Very cool. Thanks to everyone who muddled through trying to help me. This will do it!

        coreolyn