update XML file with perl or other...?

Discussion in 'Perl Misc' started by inetquestion, Oct 7, 2008.

  1. inetquestion

    inetquestion Guest

    I have an XML document which I would like to modify based on the
    results of a test. A subset of information contained in the XML file
    is shown below. If a 'test' to each server:port were to fail, then
    the xml file should be modified such that the attribute 'off' is set
    to a value of 1. I was thinking of doing this in perl, but would
    like to get some suggestions based on ease of modifying files, etc...
    I've written some basic perl scripts to read an xml file before, but
    ran into some confusion when trying to write them back out. Any
    suggestions or pointers?


    ....
    ....
    <app name="hokiepokie">
    <instance host="server01" port="8080" Off="0"/>
    <instance host="server02" port="8081" Off="0"/>
    <instance host="server03" port="8082" Off="0"/>
    </app>
    ....
    ....




    -Inet
    inetquestion, Oct 7, 2008
    #1
    1. Advertising

  2. inetquestion

    inetquestion Guest

    On Oct 6, 10:25 pm, inetquestion <> wrote:
    > I have an XML document which I would like to modify based on the
    > results of a test. A subset of information contained in the XML file
    > is shown below. If a 'test' to each server:port were to fail, then
    > the xml file should be modified such that the attribute 'off' is set
    > to a value of 1. I was thinking of doing this in perl, but would
    > like to get some suggestions based on ease of modifying files, etc...
    > I've written some basic perl scripts to read an xml file before, but
    > ran into some confusion when trying to write them back out. Any
    > suggestions or pointers?
    >
    > ...
    > ...
    > <app name="hokiepokie">
    > <instance host="server01" port="8080" Off="0"/>
    > <instance host="server02" port="8081" Off="0"/>
    > <instance host="server03" port="8082" Off="0"/>
    > </app>
    > ...
    > ...
    >
    > -Inet



    for example, lets say the test on server02 failed, what procedure
    would need to occur to change the xml file as follows? If possible I
    would prefer to do this via traditional xml methods and not text file
    parsing. I'm just a little lost as to how to do it...

    <app name="hokiepokie">
    <instance host="server01" port="8080" Off="0"/>
    <instance host="server02" port="8081" Off="1"/>
    <instance host="server03" port="8082" Off="0"/>
    </app>
    inetquestion, Oct 7, 2008
    #2
    1. Advertising

  3. inetquestion

    Guest

    inetquestion <> wrote:
    > I have an XML document which I would like to modify based on the
    > results of a test.


    You will almost certainly have to rewrite the entire file. Modifying
    an XML file in place, except in some highly rigid contexts, would be quite
    an adventure.


    > A subset of information contained in the XML file
    > is shown below. If a 'test' to each server:port were to fail, then
    > the xml file should be modified such that the attribute 'off' is set
    > to a value of 1. I was thinking of doing this in perl, but would
    > like to get some suggestions based on ease of modifying files, etc...
    > I've written some basic perl scripts to read an xml file before, but
    > ran into some confusion when trying to write them back out. Any
    > suggestions or pointers?


    If the file isn't large compared to your RAM, I'd probably start by trying
    XML::Simple, which despite the name isn't all that simple and it really
    pays to read the docs pretty thoroughly.

    >
    > ...
    > ...
    > <app name="hokiepokie">
    > <instance host="server01" port="8080" Off="0"/>
    > <instance host="server02" port="8081" Off="0"/>
    > <instance host="server03" port="8082" Off="0"/>
    > </app>


    Useful options for XML::Simple might be
    ForceArray=>1, KeyAttr=>{app=>'name', instance=>'host'}

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    The costs of publication of this article were defrayed in part by the
    payment of page charges. This article must therefore be hereby marked
    advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
    this fact.
    , Oct 7, 2008
    #3
  4. inetquestion

    Guest

    On Mon, 6 Oct 2008 19:46:33 -0700 (PDT), inetquestion <> wrote:

    >On Oct 6, 10:25 pm, inetquestion <> wrote:
    >> I have an XML document which I would like to modify based on the
    >> results of a test. A subset of information contained in the XML file
    >> is shown below. If a 'test' to each server:port were to fail, then
    >> the xml file should be modified such that the attribute 'off' is set
    >> to a value of 1. I was thinking of doing this in perl, but would
    >> like to get some suggestions based on ease of modifying files, etc...
    >> I've written some basic perl scripts to read an xml file before, but
    >> ran into some confusion when trying to write them back out. Any
    >> suggestions or pointers?
    >>
    >> ...
    >> ...
    >> <app name="hokiepokie">
    >> <instance host="server01" port="8080" Off="0"/>
    >> <instance host="server02" port="8081" Off="0"/>
    >> <instance host="server03" port="8082" Off="0"/>
    >> </app>
    >> ...
    >> ...
    >>
    >> -Inet

    >
    >
    >for example, lets say the test on server02 failed, what procedure
    >would need to occur to change the xml file as follows? If possible I
    >would prefer to do this via traditional xml methods and not text file
    >parsing. I'm just a little lost as to how to do it...
    >
    > <app name="hokiepokie">
    > <instance host="server01" port="8080" Off="0"/>
    > <instance host="server02" port="8081" Off="1"/>
    > <instance host="server03" port="8082" Off="0"/>
    ></app>


    Here is one approach. This avoids regenerating the entire xml file
    from scratch. The raw xml is kept intact. Only the lines you pick and
    modify are changed.

    The capture buffers are just array's of sequence number which point to a
    central data repository. The Dump function uses the sequence number
    to display the raw data. Modifications are made using the sequence reference.

    This will be released soon.

    sln

    ===========================================================

    <<XML;
    <!--
    Notes: This xml file contains server/port information.
    -->
    <root>
    ....
    ....
    <app name="hokiepokie">
    <instance host="server01" port="8080" Off="0"/>
    <instance host="server02" port="8081" Off="0"/>
    <instance host="server03" port="8082" Off="0"/>
    </app>
    ....
    ....
    </root>
    XML



    # Your program.pl
    # ------------

    use strict;
    use warnings;

    use RXParse; # VERSIN 2

    my $p = new RXParse();

    sub starth
    {
    my ($obj, $el, $term, @attr) = @_;
    my $buffer = lc($el);
    if ($buffer eq 'instance')
    {
    $obj->CaptureOn ( $buffer);
    }
    }
    sub endh
    {
    my ($obj, $el, $term) = @_;
    my $buffer = lc($el);
    if ($buffer eq 'instance')
    {
    $obj->CaptureOff ( $buffer, 1);
    }
    }

    $p->setMode( 'resume_onerror'=> 1 );
    $p->setHandlers ( 'start' => \&starth, 'end' => \&endh);

    my $fname = 'c:\temp\hokie.xml';
    open my $fh, $fname or die "can't open $fname ...";

    $p->CaptureOn ( 'ALL');
    my $parse_errors = $p->parse ( $fh);
    $p->CaptureOff ( 'ALL');

    print STDERR "Parse errors = $parse_errors\n";
    close $fh;

    $p->DumpCaptureBuffs (); # to view buffers


    ## Process the 'instance' buffer raw data. Can use rxparse built-ins if needed.
    ## There are many ways to do this, this is just one.
    ## ...


    ## Xml-Simple example, straight forward but not tested
    #
    if (0)
    {
    use XML::Simple;

    ## Get 'instance' buffer ref's to its raw data
    my @instrefs = $p->GetCaptureBuffer ( 'instance'); # this function is not firm yet


    ## Process it
    foreach my $iref (@instrefs)
    {
    if (defined $$iref) # In this case it will always be defined
    {
    my $simpref = XMLin ( $$iref, SuppressEmpty => '');
    my ($host, $port, $off) = ($simpref->{host}, $simpref->{port}, $simpref->{off});

    ## Check the host/port status for on/off
    if (1) {
    $simpref->{off} = 1; # Turn it off
    } else {
    $simpref->{off} = 0; # Turn it on (or skip if on by default)
    }
    ## Write it back to the instance buffer (if 'off' modified)
    $$iref = XMLout ( $simpref);
    }

    }
    ## All done, write the 'all' buffer out to a file (if 'off' modified)
    if (1)
    {
    my $fname = 'c:\temp\hokie_new.xml';
    open my $fh, $fname or die "can't open $fname ...";
    $p->WriteCaptureBuffer ( 'all', $fh); # this function is not firm yet.
    close $fh; # can pass in file handle or ref to recieving buf.
    }
    }

    __END__



    BUFFER: instance
    =====================================
    index seqence
    ----- --------
    [0] 2 <instance host="server01" port="8080" Off="0"/>
    [1] 4 <instance host="server02" port="8081" Off="0"/>
    [2] 6 <instance host="server03" port="8082" Off="0"/>



    BUFFER: all
    =====================================
    index seqence
    ----- --------
    [0] 1 <!--
    Notes: The file contains server/port information.
    -->
    <root>
    ....
    ....
    <app name="hokiepokie">

    [1] -2
    [2] 3

    [3] -4
    [4] 5

    [5] -6
    [6] 7
    </app>
    ....
    ....
    </root>
    , Oct 7, 2008
    #4
  5. inetquestion

    mirod Guest

    inetquestion wrote:
    > On Oct 6, 10:25 pm, inetquestion <> wrote:
    >> I have an XML document which I would like to modify based on the
    >> results of a test. A subset of information contained in the XML file
    >> is shown below. If a 'test' to each server:port were to fail, then
    >> the xml file should be modified such that the attribute 'off' is set
    >> to a value of 1. I was thinking of doing this in perl, but would
    >> like to get some suggestions based on ease of modifying files, etc...
    >> I've written some basic perl scripts to read an xml file before, but
    >> ran into some confusion when trying to write them back out. Any
    >> suggestions or pointers?


    You could use XML::Twig. Build an XPath-like expression that matches
    the element you want to update and when you reach it update the attribute.
    The twig_roots/twig_print_outside_roots combo ensures that everything else gets
    outputed untouched:

    #!/usr/bin/perl

    use strict;
    use warnings;

    use XML::Twig;

    # clever argument passing ;--)
    my $xml = shift( @ARGV) || 'inet.xml';
    my $app = shift( @ARGV) || 'hokiepokie';
    my $host = shift( @ARGV) || 'server02';
    my $port = shift( @ARGV) || '8081';

    # the XPath expression that matches the element to update
    my $trigger= qq{app[\@name="$app"]/instance[\@host="$host" and \@port="$port"]};

    XML::Twig->new( twig_roots => {$trigger => \&switch_state, },
    twig_print_outside_roots => 1, # everything else is untouched
    )
    ->parsefile_inplace( $xml); # not really inplace, just looks like it

    sub switch_state
    { my( $t, $instance)= @_;
    $instance->set_att( Off => 1); # instance is the element object
    $instance->print;
    }

    --
    mirod
    mirod, Oct 8, 2008
    #5
  6. inetquestion

    Todd Wade Guest

    On Oct 6, 10:25 pm, inetquestion <> wrote:
    > I have an XML document which I would like to modify based on the
    > results of a test.  A subset of information contained in the XML file
    > is shown below.  If a 'test' to each server:port were to fail, then
    > the xml file should be modified such that the attribute 'off' is set
    > to a value of 1.    I was thinking of doing this in perl, but would
    > like to get some suggestions based on ease of modifying files, etc...
    > I've written some basic perl scripts to read an xml file before, but
    > ran into some confusion when trying to write them back out.  Any
    > suggestions or pointers?
    >
    > ...
    > ...
    > <app name="hokiepokie">
    >     <instance host="server01" port="8080" Off="0"/>
    >     <instance host="server02" port="8081" Off="0"/>
    >     <instance host="server03" port="8082" Off="0"/>
    > </app>
    > ...
    > ...
    >
    > -Inet


    Here is a way to do it using the XML::XPath module:

    use warnings;
    use strict;
    use XML::XPath;

    my $document = XML::XPath->new( xml => join('', <DATA>) );
    my $query = '/app/instance';
    my $instances = $document->find($query);

    foreach my $instance ( $instances->get_nodelist ) {
    my $host = $instance->findvalue('./@host');
    my $port = $instance->findvalue('./@port');

    # conditional to decide if you want to change the Off attribute
    if ( $host eq 'server02' ) {
    $instance->getAttributeNode('Off')->setNodeValue(1);
    }
    }

    print $document->findnodes_as_string('/');

    __DATA__
    <app name="hokiepokie">
    <instance host="server01" port="8080" Off="0"/>
    <instance host="server02" port="8081" Off="0"/>
    <instance host="server03" port="8082" Off="0"/>
    </app>
    Todd Wade, Oct 9, 2008
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.

Share This Page