update XML file with perl or other...?

I

inetquestion

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
 
I

inetquestion

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

xhoster

inetquestion said:
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.
 
S

sln

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

mirod

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;
}
 
T

Todd Wade

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>
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top