REVISED: Variable length array to XML

PERL

    Next

  • 1. Perl Logo
    Is there a Perl logo image I can link to from my website with the <img src=""> tag. I see that there are copyright restrictions with the Camel and the Onion. Thank You, ++imanshu
  • 2. Why is "use 5.010" necessary
    Why is it necessary to state: use 5.010; ... simply to have access to Perl 5.10's features? If I didn't want to use it's features I wouldn't have installed it in the first place. 5.10 is backwards compatible in any case so it all seems a bit pointless. zaphod
  • 3. NET::Telnet question
    Does anyone know of a way to set the default terminal type using NET::Telnet to a tvi955? The server I am telneting into has an application that executes on login and opens up a menu system. There is no command line and no way to get to the command line so I can't send any type of set term command to the server. I must log in as a tvi955 or I get a bunch of trash coming back. I can clean up the trash with a regex but it would be much better to just log in as a tvi955 in the first place. Thanks in advance for your help, Chip
  • 4. How to get at the perl options
    Hi, Is there a way to capture the perl options in a command like this: perl -w -S script.pl @ARGV holds the options script.pl & onwards. What I want are the the perl options (in this case: -w, -S ) also.

REVISED: Variable length array to XML

Postby Don Pich » Tue, 29 Mar 2011 23:29:27 GMT

've been messing with this on and off since my last post. Here is my
current code (cleaned up thanks to advice in this board, and made shorter
- Took out some unnecessary arrays):

___CODE___

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $infile = '/media/Docs/Scripts/Perl/Putty/TEST.csv';
open (CSVFILE, $infile) || die ("Could not open $infile! $!");
my @final = ();
my @temp1 = ();
my @temp2 = ();
while (my $line = <CSVFILE>) {
$line =~ tr/"\r\n//d;
$line =~ s/\\/:/g;
(my $C1,my $C2,my $C3,my $C4) = split ',', $line;
my @temp1 = split (':', $C2);
shift (@temp1);
my $tempcount = @temp1;
for (my $a = 0 ; $a <= $tempcount-1; $a++) {
push ( @temp2, $temp1[$a] );
}
push ( @temp2, $C1 );
push ( @temp2, $C4 );
my $temp2count = @temp2;
unshift ( @temp2, $temp2count );
push @final, [ @temp2 ];
$temp2count = 0;
@temp2 = ();
@temp1 = ();
}
close $infile;
exit(0);

___CODE___

Here is PART of the input

___ INPUT ___

"Default Settings","Sessions","",""
"ADMSND70AFC.01","Sessions\NOC-CO\AFC","","172.16.22.34"
"ARTHND16AFC.01","Sessions\NOC-CO\AFC","","172.16.22.26"
"CAVWND48AFC.01","Sessions\NOC-CO\AFC","","172.16.22.6"
"CRYSND04AFC.01","Sessions\NOC-CO\AFC","","172.16.22.46"
"CVLRND10AFC.01","Sessions\NOC-CO\AFC","","172.16.22.90"
"PKRVND05AFC.04 GFTN","Sessions\NOC-CO\AFC","","172.16.22.110"
"PMBNND60AFC.01","Sessions\NOC-CO\AFC","","172.16.22.78"
"STTMND02AFC.01","Sessions\NOC-CO\AFC","","172.16.22.74"
"WLCTND67AFC.01","Sessions\NOC-CO\AFC","","172.16.22.114"
"WVTNMN74AFC.01 DMAX","Sessions\NOC-CO\AFC","","172.16.22.94"
"WVTNMN74AFC.02 UMC1000","Sessions\NOC-CO\AFC","","172.16.22.102"
"PKRVND05APMAX.01","Sessions\NOC-CO\APMAX",""," XXXX@XXXXX.COM "
"PKRVND05APMAX.02","Sessions\NOC-CO\APMAX",""," XXXX@XXXXX.COM "
"DVN cisco","Sessions\NOC-CO\Cisco","","10.243.255.250"
"DYTNND01C1924.01","Sessions\NOC-CO\Cisco\1900 Series","","172.16.19.175"
"PKRVND05C1924.01","Sessions\NOC-CO\Cisco\1900 Series","","172.16.19.171"

___ INPUT ___

Here is the Output:

I want to ignore the first line ('Default' etc) and remove 'Sessions' as
putting that into this information is redundant. I also counted how may
array elements are in each array within the array (i.e. the first line
has four elements (NOC-CO,AFC,ADMSND70AFC.01,172.16.22.34). Hence, the
first array element is the count of array elements. Not necessarily sure
if it's necessary, but it's there.

What I am really having a hard time wrapping my head around is that it's
obvious that a hash is a better choice for sorting this data. I think
I'm making a mistake by actually using an array, but I understand
arrays. Hashes are simpler, but I must be missing the point.

My goal is to do as others have posted:

___ DESIRED RESULTS ___

<SESSION>
<NOC-CO>
<AFC>
<ADMSND70AFC.01>
<172.16.22.34>
</ADMSND70AFC.01>
etc...

___ DESIRED RESULTS ___

The code below should be what I need.

___ PROPOSED ADDITIONAL CODE ___

print qq(<SESSION>\n);
foreach my $k1 (sort keys %session)
{
print qq( <$k1>\n);
foreach my $k2 (sort keys %{$session{$k1}})
{
print qq( <$k2>\n);
foreach my $k3 (sort keys %{$session{$k1}{$k2}})
{
print qq( <$k3>\n);
print qq( $session{$k1}{$k2}{$k3}\n);
print

Re: REVISED: Variable length array to XML

Postby Uri Guttman » Wed, 30 Mar 2011 02:49:03 GMT

>>>>> "DP" == Don Pich < XXXX@XXXXX.COM > writes:

  DP> I've been messing with this on and off since my last post.  Here is my 
  DP> current code (cleaned up thanks to advice in this board, and made shorter 
  DP> - Took out some unnecessary arrays):

  DP> use Data::Dumper;
  DP> my $infile = '/media/Docs/Scripts/Perl/Putty/TEST.csv';
  DP> open (CSVFILE, $infile) || die ("Could not open $infile! $!");
  DP> my @final = ();
  DP> my @temp1 = ();
  DP> my @temp2 = ();

the = () are not needed.

  DP> while (my $line = <CSVFILE>) {
  DP>   $line =~ tr/"\r\n//d;
  DP>   $line =~ s/\\/:/g;
  DP>   (my $C1,my $C2,my $C3,my $C4) = split ',', $line;
  DP>   my @temp1 = split (':', $C2);

why do you redeclare @temp1? and temp is ALWAYS a bad name. choose a
name that reflects the actual data or usage of a variable.
  DP>   shift (@temp1);

no comments. you need to say WHY you are doing something like losing an
element from the array.

  DP>   my $tempcount = @temp1;
  DP>   for (my $a = 0 ; $a <= $tempcount-1; $a++) {

$a is reserved for use by sort. also a bad generic name which doesn't
say anything.

  DP>     push ( @temp2, $temp1[$a] );

all you do is copy temp1 to temp2!! that doesn't need a loop.


  DP>   }
  DP>   push ( @temp2, $C1 );
  DP>   push ( @temp2, $C4 );
  DP>   my $temp2count = @temp2;
  DP>   unshift ( @temp2, $temp2count );

with all the temps, this code is impossible to follow. there is no easy
way to tell if it is correct or what.

  DP>   push @final, [ @temp2 ];

since all you do is collect that array and its count:

	push @final, [ scalar @temp2, @temp2 ];

you have a {*filter*} for unneeded temp variables


  DP>   $temp2count = 0;
  DP>   @temp2 = ();
  DP>   @temp1 = ();

all unneeded since the first uses of them assign fresh values to them.


you never use any of the computed data. this doesn't DO anything.

  DP> I'm having a hard time wrapping my head around populating the hash.

a clue. PICK NAMES THAT MEAN SOMETHING. if the xml template has useful
names, why not use those in the code? then if you Data::Dumper the hash
tree, you can compare that to the desired outout and have a chance of
matching them up and fixing it.

also pick a templater to do the main work. look at Template::Simple
which can do this for you with the least amount of work on your part.

uri

-- 
Uri Guttman  ------   XXXX@XXXXX.COM   --------   http://www.**--****.com/  --
-----  Perl Code Review , Architecture, Development, Training, Support ------
---------  Gourmet Hot Cocoa Mix  ----   http://www.**--****.com/  ---------

Re: REVISED: Variable length array to XML

Postby Christian Winter » Wed, 30 Mar 2011 04:05:29 GMT

m 28.03.2011 16:29, schrieb Don Pich:

I wouldn't say cleaned up, rather replaced with something even
stranger.


No need to assign empty lists there.
my @final, @temp1, @temp2;
would suffice, but I'll get to that again...


If your example input code is really representative for the
whole of it, it doesn't make sense to replace the backslashes
with something else if all you do with it later is splitting
on it.


Here you could as well say
my @temp1 = split /\\/, $C2;

Though now I'm scratching my head why you declare @temp1
outside of the loop without using it and redeclare it
on the inside again.


That's a rather roundabout way for
@temp2 = @temp1;


The syntax of push is
push ARRAY,LIST
so it lets you say
push @temp2, $C1, $C4;


Why are you switching to @temp2 at all, if you don't use
@temp1 anyway? Everything after the "shift (@temp1)" line
could be condensed to
push @temp1, $C1, $C4;
unshift @temp1, scalar(@temp1);

if you write


push @final, [@temp1];


Unneccessary, it gets overwritten each loop anyway.


And ditto for @temp1, it's defined with "my" inside the scope
of the loop, so when the loop ends it goes out of scope, and in
the next iteration your @temp1 will be a {*filter*} array.


All in all, your loop can be reduced to:

while (my $line =<CSVFILE>) {
$line =~ tr/"\r\n//d;
my @c = split /,/, $line;
my @temp1 = split /\\/, $c[1];
shift (@temp1);
push ( @temp1, $c[0], $c[3] );
unshift ( @temp1, scalar @temp1 );
push @final, [ @temp1 ];
}


You can do that by either reading from <INFILE> once outside
the loop or by adding
next if( $. == 1 );
inside. See "perldoc perlvar" for the exact meaning of "$."


It isn't, getting the length of the arrays is cheap.


Not neccessarily. Hashes are flexible and save memory if you have
deep structures with identically named branches, and they win out
if you need to access your data by names instead of ordinals.


The "etc..." is what makes it impossible to suggest a precise solution.

Do you rather want to it read

<SESSION>
<NOC-CO>
<AFC>
<ADMSND70AFC.01>
<172.16.22.34>
</ADMSND70AFC.01>
</AFC>
<NOC-CO>
<NOC-CO>
<AFC>
<ARTHND16AFC.01>
<172.16.22.26>
</ARTHND16AFC.01>
</AFC>
<NOC-CO>
</SESSION>

or do you want to get
<SESSION>
<NOC-CO>
<AFC>
<ADMSND70AFC.01>
<172.16.22.34>
</ADMSND70AFC.01>
<ARTHND16AFC.01>
<172.16.22.26>
</ARTHND16AFC.01>
</AFC>
<NOC-CO>
</SESSION>

? Both are xml.

If you want to go for the first one, arrays are just fine.
If you want the second one, a hash may be more fitting, as its
structure already reflects what the output will be - and there
are xml modules that can take a hash and output xml in one go.


Or might not be what you want, considering that it can only
handle data with fixed depth.


You can either do as I showed in my other reply, populate your leaf's
hash structure bottom up and merge it with the main hash by using a
module, or you've got to work a lot with references and give yourself
a serious headache ;)

I'll try to put together a small working piec

Re: REVISED: Variable length array to XML

Postby Tad McClellan » Wed, 30 Mar 2011 04:44:45 GMT






Yuk!

One declaration with 4 variables is easier to read than 4 declarations.

A regular expression should *look like* a regular expression.

    my($C1, $C2, $C3, $C4) = split /,/, $line;

But sequentially-numbered variable names are almost always a
red flag that indicates you should be using an array instead.

    my @columns = split /,/, $line;




What a worthless choice of variable name!

Choose a more valuable name.

Above you use split without parens, and here you use split with parens.

Decide if you like parens or not, then consistently always use them or
don't use them.

If you don't want the 1st element, then don't save the first element:

    my(undef, @session_parts) = split /:/, $columns[1];




Yet another poorly chosen variable name.

$a is used for sort()ing, so you should not use it for other purposes.

Take the time to come up with _meaningful_ names, it will help
you understand and debug your code more easily.

You should let perl do the indexing for you:

    foreach my $i ( 0..@session_parts ) {




Even better, don't do any indexing at all!

    foreach my $part (@session_parts) {
        push @temp2, $part;
    }


Best is to not even use unnecessary loops:

    @temp2 = @session_parts;


-- 
Tad McClellan
email: perl -le "print scalar reverse qq/moc.liamg\100cm.j.dat/"
The above message is a Usenet post.
I don't recall having given anyone permission to use it on a Web site.

Re: REVISED: Variable length array to XML

Postby John W. Krahn » Wed, 30 Mar 2011 09:06:26 GMT



And reduce it further:


while ( my $line = <CSVFILE> ) {
     $line =~ tr/"\r\n//d;
     my @c = split /,/, $line;
     my ( undef, @temp1 ) = split /\\/, $c[ 1 ];
     push @final, [ 2 + @temp1, @temp1, @c[ 0, 3 ] ];
}



John
-- 
Any intelligent fool can make things bigger and
more complex... It takes a touch of genius -
and a lot of courage to move in the opposite
direction.                   -- Albert Einstein

Re: REVISED: Variable length array to XML

Postby Don Pich » Thu, 31 Mar 2011 01:57:04 GMT

hank you for the great advice.

Here is my current code:

___ CODE ___
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my $infile = '/media/Docs/Scripts/Perl/Putty/putty.csv.back';
open (CSVFILE, $infile) || die ("Could not open $infile! $!");

my @final;

while ( my $line = <CSVFILE> ) {
$line =~ tr/"\r\n//d;
my @cleanline = split /,/, $line;
my ( undef, @prefinal ) = split /\\/, $cleanline[ 1 ];
push @final, [ @prefinal, @cleanline[ 0, 3 ] ];
}
close $infile;

my %hash;
foreach my $leaf (@final) {
my $ptr = \%hash;
foreach my $i ( 0 .. $#$leaf - 1 ) {
my $node = $leaf->[$i];
if( $i != $#$leaf - 1 ) {
$ptr->{$node} = {}
unless( exists $ptr->{$node} );
$ptr = $ptr->{$node};
} else {
$ptr->{$node} = $leaf->[$i + 1];
}
}
}

print qq(<SESSION>\n);
foreach my $k1 (sort keys %hash) {
print qq( <$k1>\n);
foreach my $k2 (sort keys %{$hash{$k1}}) {
print qq( <$k2>\n);
foreach my $k3 (sort keys %{$hash{$k1}{$k2}}) {
print qq( <$k3>\n);
foreach my $k4 (sort keys %{$hash{$k1}{$k2}{$k3}}) {
print qq( <$k4>\n);
foreach my $k5 (sort keys %{$hash{$k1}{$k2}{$k3}{$k4}}) {
print qq( <$k5>\n);
foreach my $k6 (sort keys %{$hash{$k1}{$k2}{$k3}{$k4}{$k5}}) {
print qq( <$k6>\n);
foreach my $k7 (sort keys %{$hash{$k1}{$k2}{$k3}{$k4}{$k5}
{$k6}}) {
print qq( <$k7>\n);
print qq( $hash{$k1}{$k2}{$k3}{$k4}{$k5}{$k6}{$k7}
\n);
print qq( </$k7>\n);
}
print qq( </$k6>\n)
}
print qq( </$k5>\n);
}
print qq( </$k4>\n);
}
print qq( </$k3>\n);
}
print qq( </$k2>\n);
}
print qq( </$k1>\n);
}
print qq(</SESSION>\n);

# Print Entire HASH
#print Dumper %hash;

# Print Entire Array
#for my $i ( 1 .. $#final ) {
# print "@{$final[$i]} \n";
#}
exit(0);

___ CODE ___

The data in putty.csv.back is this:

____ INPUT ___

"LNKN.41F.01.01","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.01",""," XXXX@XXXXX.COM "
"LNKN.41F.01.02","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.02",""," XXXX@XXXXX.COM "
"LNKN.41F.01.03","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.03",""," XXXX@XXXXX.COM "
"LNKN.41F.01.04","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.04vacant",""," XXXX@XXXXX.COM "
"LNKN.41F.01.05","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.05vacant",""," XXXX@XXXXX.COM "
"LNKN.41F.01.06","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.06vacant",""," XXXX@XXXXX.COM "
"LNKN.41F.01.07","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.07vacant",""," XXXX@XXXXX.COM "
"LNKN.41F.01.09","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.09vacant",""," XXXX@XXXXX.COM "
"LNKN.41F.01.10","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.10vacant",""," XXXX@XXXXX.COM "
"LNKN.41F.01.11","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41F.01
\LNKN.41F.01.11vacant",""," XXXX@XXXXX.COM "
"LNKN.41F.01.12","Sessions\NOC-CO\Occam\LakotaRing\LNKN\LNKN.41

Re: REVISED: Variable length array to XML

Postby Uri Guttman » Thu, 31 Mar 2011 05:51:44 GMT

gt;>>>> "DP" == Don Pich < XXXX@XXXXX.COM > writes:

DP> Thank you for the great advice.

looks like you didn't take any of my advice. a template system is the
way to go here. you are reinventing a general purpose wheel in a very
specific and bad way. your code could be reduced to about 30 lines or
less with Template::Simple.

DP> Here is my current code:


DP> my %hash;
DP> foreach my $leaf (@final) {
DP> my $ptr = \%hash;
DP> foreach my $i ( 0 .. $#$leaf - 1 ) {
DP> my $node = $leaf->[$i];
DP> if( $i != $#$leaf - 1 ) {
DP> $ptr->{$node} = {}
DP> unless( exists $ptr->{$node} );
DP> $ptr = $ptr->{$node};
DP> } else {
DP> $ptr->{$node} = $leaf->[$i + 1];

learn about autovivification. there is no need to manually make a hash
ref if one doesn't exist. assume it does and perl creates it for you.

DP> }
DP> }
DP> }

DP> print qq(<SESSION>\n);
DP> foreach my $k1 (sort keys %hash) {
DP> print qq( <$k1>\n);
DP> foreach my $k2 (sort keys %{$hash{$k1}}) {
DP> print qq( <$k2>\n);
DP> foreach my $k3 (sort keys %{$hash{$k1}{$k2}}) {
DP> print qq( <$k3>\n);
DP> foreach my $k4 (sort keys %{$hash{$k1}{$k2}{$k3}}) {
DP> print qq( <$k4>\n);
DP> foreach my $k5 (sort keys %{$hash{$k1}{$k2}{$k3}{$k4}}) {
DP> print qq( <$k5>\n);

none of my great advice on using refs here was taken. this is unreadable
and unmaintainable code.

<massive snip>

if your data and structures are so simple and short you need only about
5 lines to show it works or not. pasting large amounts of input and
output is annoying.

DP> This is getting very close. Here is the tricky part now. I need to
DP> calculate the length of the hash. Reading several docs, the proper
DP> method for calculating the length of elements in a hash is:

DP> my $size = scalar(keys %hash);

there is no such thing as the length of hash. and scalar is not needed
there as the scalar assignment will force scalar context on keys.

DP> I will need to implement some form of this because my data is variable
DP> (I.E., I can have a hash 4 elements long, or as above, it would be 8
DP> elements long). My plan is to create some 'if' loops. So in a very
DP> generic sense. I'm trying to have the script make a decision that says
DP> if the hash is 8 elements long, it will produce a leaf that is 8 elements
DP> long. If it has 4 elements, it will produce a leaf that is 4 elements
DP> long.

again, a template will do all of that for you.

DP> I don't think my script will calculate the proper width for the element.
DP> This works fine for a simple hash. But a hash of hash seems like it's
DP> very difficult to calculate.

that makes no sense as usual. hashes don't have width. if you can do
anything on a hash, you can do it on a hash of hashes. it is just
another hash lower down. you can't see to separate that concept yet.

again, switch to a templater. you are going down a very bad path now. if
i had the serious interest i would code this up for you. but you are
ignoring my comments it seems so i will ignore your needs.

uri

--
U

Re: REVISED: Variable length array to XML

Postby Christian Winter » Thu, 31 Mar 2011 13:49:39 GMT

Am 29.03.2011 22:51, schrieb Uri Guttman:

As the one who wrote that piece of code I've got to add my
2 Ct here. Perl does do autovivication, but in this case it
will not DWIM.
     $ptr = $ptr->{$node};
will *not* autovivicate a hash entry with the key "$node". There is
neither an assignment to the hash entry, nor does the code try to
access a deeper entry that would bring intermediate entries to life.

Making $ptr point to an undef value (that would happen with each
iteration if the hash ref wasn't assigned manually), perl *would*
autovivicate a hash reference - but it would have no association
with our %hash, and only in the last inner iterations would the
(short-lived) referenced hash even hold an entry with the key "$node".

Or more concise from perlfaq4:
| Normally, merely accessing a key's value for a nonexistent key
| does *not* cause that key to be forever there.

-Chris

Re: REVISED: Variable length array to XML

Postby Uri Guttman » Thu, 31 Mar 2011 14:49:44 GMT

>>>>> "CW" == Christian Winter < XXXX@XXXXX.COM > writes:

  CW> Am 29.03.2011 22:51, schrieb Uri Guttman:
  >>>>>>> "DP" == Don Pich< XXXX@XXXXX.COM >  writes:
  >> 
  DP> my %hash;
  DP> foreach my $leaf (@final) {
  DP> my $ptr = \%hash;
  DP> foreach my $i ( 0 .. $#$leaf - 1 ) {
  DP> my $node = $leaf->[$i];
  DP> if( $i != $#$leaf - 1 ) {
  DP> $ptr->{$node} = {}
  DP> unless( exists $ptr->{$node} );
  DP> $ptr = $ptr->{$node};
  DP> } else {
  DP> $ptr->{$node} = $leaf->[$i + 1];
  >> 
  >> learn about autovivification. there is no need to manually make a hash
  >> ref if one doesn't exist. assume it does and perl creates it for you.

  CW> As the one who wrote that piece of code I've got to add my
  CW> 2 Ct here. Perl does do autovivication, but in this case it
  CW> will not DWIM.
  CW>     $ptr = $ptr->{$node};
  CW> will *not* autovivicate a hash entry with the key "$node". There is
  CW> neither an assignment to the hash entry, nor does the code try to
  CW> access a deeper entry that would bring intermediate entries to life.

true. i didn't delve into the logic much. it was the 
	$ptr->{$node} = {}
line which i was triggered on. that is almost never needed due to
autoviv. the fact that his code didn't use that was confusing. in fact
it is odd what he is doing there. why do $ptr = $ptr->{$node}; ? it just
gets the new anon hash into $ptr. i can't make head nor tail of what he
is trying to do in that loop.

  CW> Making $ptr point to an undef value (that would happen with each
  CW> iteration if the hash ref wasn't assigned manually), perl *would*
  CW> autovivicate a hash reference - but it would have no association
  CW> with our %hash, and only in the last inner iterations would the
  CW> (short-lived) referenced hash even hold an entry with the key "$node".

i was making that same point in a recent thread on autoviv and i know it
well. the code is just too odd looking anyhow. as i keep telling him,
use a templater.

uri

-- 
Uri Guttman  ------   XXXX@XXXXX.COM   --------   http://www.**--****.com/  --
-----  Perl Code Review , Architecture, Development, Training, Support ------
---------  Gourmet Hot Cocoa Mix  ----   http://www.**--****.com/  ---------

Re: REVISED: Variable length array to XML

Postby Christian Winter » Thu, 31 Mar 2011 15:46:35 GMT

Am 30.03.2011 07:49, schrieb Uri Guttman:
 >

It's basically just like a pointer in a C-style tree. At first
it points to the root node, then with each iteration a child node
gets added (if not already there) and the pointer is moved to point to
this child. The fact that it uses the name of a hash key instead of
a C struct member's name is just a variation of the concept.


I'm not convinced that a template system is the only answer to
the problem. A CSV module and Hash::Merge would be enough to populate
the hash structure, and one of the numerous xml modules should be
able to output what he wants - though that could be done in a few
lines of code as well. In fact, it should be nothing more than

dump_hash(\%hash);

sub dump_hash
{
   my($h, $ind) = @_;
   my $indent = "  " x $ind;
   return (ref $h eq "HASH") ?
     join('', map {
       $indent . "<$_>" . $/ .
       dump_hash($h->{$_}, $ind + 1) .
       $indent . "</$_>" . $/
     } keys %$h)
     :
     $indent . $h . $/;
}

I myself would, of course, use appropriate modules for production
code which check for corner cases and provide sensible error messages,
but the OPs problem and the possible solutions do make for a fine
academic task to learn about (or toy around with) topics like hashes,
references, recursion, top-down vs. bottom-up concepts or variable
depth algorithms.

-Chris

Re: REVISED: Variable length array to XML

Postby Uri Guttman » Thu, 31 Mar 2011 16:27:49 GMT

>>>>> "CW" == Christian Winter < XXXX@XXXXX.COM > writes:

  CW> Am 30.03.2011 07:49, schrieb Uri Guttman:
  >>>>>>> "CW" == Christian Winter< XXXX@XXXXX.COM >  writes:
  >> 
  >> true. i didn't delve into the logic much. it was the
  >> $ptr->{$node} = {}
  >> line which i was triggered on. that is almost never needed due to
  >> autoviv. the fact that his code didn't use that was confusing. in fact
  >> it is odd what he is doing there. why do $ptr = $ptr->{$node}; ? it just
  >> gets the new anon hash into $ptr. i can't make head nor tail of what he
  >> is trying to do in that loop.

  CW> It's basically just like a pointer in a C-style tree. At first
  CW> it points to the root node, then with each iteration a child node
  CW> gets added (if not already there) and the pointer is moved to point to
  CW> this child. The fact that it uses the name of a hash key instead of
  CW> a C struct member's name is just a variation of the concept.

i know that concept well. why he needed to assign {} is the
question. you can use autoviv to build that sort of thing.

  >> i was making that same point in a recent thread on autoviv and i know it
  >> well. the code is just too odd looking anyhow. as i keep telling him,
  >> use a templater.

  CW> I'm not convinced that a template system is the only answer to
  CW> the problem. A CSV module and Hash::Merge would be enough to populate
  CW> the hash structure, and one of the numerous xml modules should be
  CW> able to output what he wants - though that could be done in a few
  CW> lines of code as well. In fact, it should be nothing more than

  CW> dump_hash(\%hash);

  CW> sub dump_hash
  CW> {
  CW>   my($h, $ind) = @_;
  CW>   my $indent = "  " x $ind;
  CW>   return (ref $h eq "HASH") ?
  CW>     join('', map {
  CW>       $indent . "<$_>" . $/ .
  CW>       dump_hash($h->{$_}, $ind + 1) .
  CW>       $indent . "</$_>" . $/
  CW>     } keys %$h)
  CW>     :
  CW>     $indent . $h . $/;
  CW> }

  CW> I myself would, of course, use appropriate modules for production
  CW> code which check for corner cases and provide sensible error messages,
  CW> but the OPs problem and the possible solutions do make for a fine
  CW> academic task to learn about (or toy around with) topics like hashes,
  CW> references, recursion, top-down vs. bottom-up concepts or variable
  CW> depth algorithms.

a templater would be easier to see the xml layout (and xslt is just a
super fancy templater of sorts). but the code he used to descend the
tree was horrible in all sorts of ways.

and your code doesn't handle arrays which his data has. Template::Simple
can do all that with almost no effort.

uri

-- 
Uri Guttman  ------   XXXX@XXXXX.COM   --------   http://www.**--****.com/  --
-----  Perl Code Review , Architecture, Development, Training, Support ------
---------  Gourmet Hot Cocoa Mix  ----   http://www.**--****.com/  ---------

Similar Threads:

1.REVISED: Variable length array to XML

I've been messing with this on and off since my last post.  Here is my 
current code (cleaned up thanks to advice in this board, and made shorter 
- Took out some unnecessary arrays):

___CODE___

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $infile = '/media/Docs/Scripts/Perl/Putty/TEST.csv';
open (CSVFILE, $infile) || die ("Could not open $infile! $!");
my @final = ();
my @temp1 = ();
my @temp2 = ();
while (my $line = <CSVFILE>) {
  $line =~ tr/"\r\n//d;
  $line =~ s/\\/:/g;
  (my $C1,my $C2,my $C3,my $C4) = split ',', $line;
  my @temp1 = split (':', $C2);
  shift (@temp1);
  my $tempcount = @temp1;
  for (my $a = 0 ; $a <= $tempcount-1; $a++) {
    push ( @temp2, $temp1[$a] );
  }
  push ( @temp2, $C1 );
  push ( @temp2, $C4 );
  my $temp2count = @temp2;
  unshift ( @temp2, $temp2count );
  push @final, [ @temp2 ];
  $temp2count = 0;
  @temp2 = ();
  @temp1 = ();
}
close $infile;
exit(0);

___CODE___

Here is PART of the input

___ INPUT ___

"Default Settings","Sessions","",""
"ADMSND70AFC.01","Sessions\NOC-CO\AFC","","172.16.22.34"
"ARTHND16AFC.01","Sessions\NOC-CO\AFC","","172.16.22.26"
"CAVWND48AFC.01","Sessions\NOC-CO\AFC","","172.16.22.6"
"CRYSND04AFC.01","Sessions\NOC-CO\AFC","","172.16.22.46"
"CVLRND10AFC.01","Sessions\NOC-CO\AFC","","172.16.22.90"
"PKRVND05AFC.04 GFTN","Sessions\NOC-CO\AFC","","172.16.22.110"
"PMBNND60AFC.01","Sessions\NOC-CO\AFC","","172.16.22.78"
"STTMND02AFC.01","Sessions\NOC-CO\AFC","","172.16.22.74"
"WLCTND67AFC.01","Sessions\NOC-CO\AFC","","172.16.22.114"
"WVTNMN74AFC.01 DMAX","Sessions\NOC-CO\AFC","","172.16.22.94"
"WVTNMN74AFC.02 UMC1000","Sessions\NOC-CO\AFC","","172.16.22.102"
"PKRVND05APMAX.01","Sessions\NOC-CO\APMAX",""," XXXX@XXXXX.COM "
"PKRVND05APMAX.02","Sessions\NOC-CO\APMAX",""," XXXX@XXXXX.COM "
"DVN cisco","Sessions\NOC-CO\Cisco","","10.243.255.250"
"DYTNND01C1924.01","Sessions\NOC-CO\Cisco\1900 Series","","172.16.19.175"
"PKRVND05C1924.01","Sessions\NOC-CO\Cisco\1900 Series","","172.16.19.171"

___ INPUT ___

Here is the Output:

I want to ignore the first line ('Default' etc) and remove 'Sessions' as 
putting that into this information is redundant.  I also counted how may 
array elements are in each array within the array  (i.e.  the first line 
has four elements (NOC-CO,AFC,ADMSND70AFC.01,172.16.22.34).  Hence, the 
first array element is the count of array elements.  Not necessarily sure 
if it's necessary, but it's there.

What I am really having a hard time wrapping my head around is that it's 
obvious that a hash is a better choice for sorting this data.  I think 
I'm making a mistake by actually using an array, but I understand 
arrays.  Hashes are simpler, but I must be missing the point.  

My goal is to do as others have posted:

___ DESIRED RESULTS ___

<SESSION>
 <NOC-CO>
  <AFC>
   <ADMSND70AFC.01>
    <172.16.22.34>
   </ADMSND70AFC.01>
etc...

___ DESIRED RESULTS ___

The code below should be what I need.

___ PROPOSED ADDITIONAL CODE ___

print qq(<SESSION>\n);
foreach my $k1 (sort keys %session)
{
    print qq(  <$k1>\n);
    foreach my $k2 (sort keys %{$session{$k1}})
    {
        print qq(    <$k2>\n);
        foreach my $k3 (sort keys %{$session{$k1}{$k2}})
        {
            print qq(      <$k3>\n);
            print qq(        $session{$k1}{$k2}{$k3}\n);
            print qq(      </$k3>\n);
        }
        print qq(    </$k2>\n);
    }
    print qq(  </$k1>\n);
}
print qq(</SESSION>\n);

___ PROPOSED ADDITIONAL CODE ___

I'm having a hard time wrapping my head around populating the hash.

2.Flexible array member + variable length array

Hi all,

With this structure that records the length of an array of pointers as its
first member:

struct array {
  ptrdiff_t length;
  void      *ptr[];
};

How does one initialise this structure using a variable length array?

ptrdiff_t len=10;
{
  struct array a[len+1];
  ...
}

It appears the above code will only work to reserve space for len pointers
if the sizeof length is the same as sizeof pointers to void and there is
no padding between length and the pointers to void. Is there portable C99
syntax that I have overlooked? (or will I have to create a VLA of type
void * and keep casting the first argument to ptrdiff_t?)

Thanks,
Adam

3.[thanks] Flexible array member + variable length array

Kevin Bracey < XXXX@XXXXX.COM > wrote:
> In message < XXXX@XXXXX.COM >
>           "S.Tobias" < XXXX@XXXXX.COM > wrote:

> > I think you use your logic in the wrong direction.  My reasoning
> > would be that since the Standard doesn't forbid structs with FAM
> > being elements of an array, they are allowed.  It means that on
[snip]

> Wrong. 6.7.2.1p2:

>    "...the last member of a structure with more than one named member
>     may have incomplete array type; such a structure ... shall not be a
>     member of a structure or an element of an array."

Shame on me having missed that part!  Many thanks!

-- 
Stan Tobias
mailx `echo  XXXX@XXXXX.COM  | sed s/[[:upper:]]//g`

4.Variable length array of arrays into tree structure - help

I need help figuring out how to print this properly.

I have generated an array of arrays from a csv file.  Here is an example 
of the data stored in the array (note:  the ' - ' are just element 
dividers and not actual elements.  They are just text).

ADMSND70AFC.01 - Sessions - NOC-CO - AFC - 172.16.22.34
ARTHND16AFC.01 - Sessions - NOC-CO - AFC - 172.16.22.26
CAVWND48AFC.01 - Sessions - NOC-CO - AFC - 172.16.22.6
CRYSND04AFC.01 - Sessions - NOC-CO - AFC - 172.16.22.46
CVLRND10AFC.01 - Sessions - NOC-CO - AFC - 172.16.22.90
DYTNND01AFC.01 - Sessions - NOC-CO - AFC - 172.16.22.50
co-gateway - Sessions - NOC-CO - Servers - 10.70.64.33
Genview-EMS - Sessions - NOC-CO - Servers - 172.16.19.254
Mayberry (external) - Sessions - NOC-CO - Servers - 1.3.5.4
Mayberry (internall) - Sessions - NOC-INT - Servers - 172.16.18.103
OPIE2 (SUN NTP SERVER) - Sessions - NOC-INT - Servers - 172.16.18.102
10008PKRV_L - Sessions - NOC-INT - Core - Rotuers - Console - 
donp: XXXX@XXXXX.COM 
10008PKRV_R - Sessions - NOC-INT - Core - Rotuers - Console - 
donp: XXXX@XXXXX.COM 

What I need to do is setup printing of this data in the following tree 
format:

Sessions
 >NOC-CO
    >AFC
      >ADMSND70AFC.01
        >172.16.22.34
      >ARTHND16AFC.01
        >172.16.22.26
      >CAVWND48AFC.01
        >172.16.22.6
      >CRYSND04AFC.01
        >172.16.22.46
      >CVLRND10AFC.01
        >172.16.22.90
      >DYTNND01AFC.01
        >172.16.22.50
    >Servers
      >co-gateway
        >10.70.64.33
      >Genview-EMS
        >172.16.19.254
      >Mayberry (external)
        >1.3.5.4
  >NOC-INT
    >Servers
      >Mayberry (internall)
        >172.16.18.103
      >OPIE2 (SUN NTP SERVER)
        >172.16.18.102
    >Core
      >Routers
        >Console
          >PKRV_L
            >10.10.17.68
          >PKRV_R
            >10.10.17.68

I.E.
- '172.16.22.34' is a child of 'ADMSND70AFC.01', which is a child of 
'AFC', which is a child of 'NOC-CO', which is a child of 'Sessions'.
- '10.10.17.68' is a child of 'PKRV_R', which is a child of 'Console', 
which is a child of 'Routers', which is a child of 'Core', which is a 
child of 'NOC-INT', which is a child of 'Session'.

I am going to place this in a XML for bookmarks for KDE Konsole.  I need 
to create a tree/folder structure.

The array inside the array ('row' - Sorry for calling it that, but it 
fits) can be variable length.

I've been looking for some form of 'tree' function or script with no luck.

Anyone have any advice?

5.Variable length arrays Q

In f(), I am passing a parameter of array type - /size/ indicates the
array's length:

void f(char arr[], int size);

In the c99 stds, it mentions the use of [*] and says (I think) that it
can be used to indicate that an array parameter has /variable length/ -
so, f() may be rewitten as:

void f(char arr[*], int size);

So, I think my understanding of [*] is wrong, as I cannot see that this
adds anything.  Unless it's maybe a commenting mnemonic of some type -
to inform the programmer that arr's length isn't fixed.  However, in
that case, one wonders why the presence of /size/ doesn't do the trick?

Could someone please tell me what [*] is for then?

x

Jo

6. Return types and variable length arrays

7. Variable length array confusion

8. variable length array (was: question about dynamic memory allocation)



Return to PERL

 

Who is online

Users browsing this forum: No registered users and 85 guest