I have been looking for examples of converting hash's to XML and back again. Initially for overlaying setting's onto objects. I started using XML::Simple, but was discouraged as at some point I may wish to embed binary data in my XML processing functions. I thought it would be bettter to start using LibXML and build on that!
I have come up with 2 example scripts, one that show's coversion of a hash to XML, the other that reads this input and prepares it for overlay on a hash. If the scripts are executed in order the first one should provide the input to the second.
These are only a starting point and not my final functions that I need to create, however they provide all the basic function that I require. I was wondering what the Monks thought of these though and before I proceed any further if there was another approach that may work better?
use strict;
use XML::LibXML;
my %testHash;
## create a test hash
$testHash{root}{testEl1}{currentValue} = 0;
$testHash{root}{testEl1}{comment} = "System test element 1";
$testHash{root}{testEl1}{enable} = 1;
$testHash{root}{testEl2}{currentValue} = 0;
$testHash{root}{testEl2}{comment} = "System test element 2";
$testHash{root}{testEl2}{enable} = 1;
$testHash{root}{testEl2}{newLevel}{enable1} = 1;
$testHash{root}{testEl2}{newLevel}{enable2} = 1;
$testHash{root}{testEl2}{newLevel}{enable3} = 1;
$testHash{root}{testEl3}{newLevel}{enable} = 1;
$testHash{root}{testEl2}{reference} = $testHash{root}{testEl1};
## call the hash processor!
my $string = myHash2Xml( \%testHash );
## create the output XML
open FILEOUT, ">myHash2Xml.xml";
print FILEOUT "$string\n";
close FILEOUT;
## end of script
## covert our hash to XML, this is a wrapper to create the
## document and pass it back as a string
sub myHash2Xml {
my ($hash) = @_;
## create a new xml document!
my $xmlDoc = XML::LibXML::Document->new( '1.0', 'UTF-8' );
## create the root element and a pointer to it!
my $root = $xmlDoc->createElement("testDoc");
$xmlDoc->setDocumentElement($root);
examineHash( $hash, \$xmlDoc, \$root ); # process the hash
my $xmlString = $xmlDoc->toString(1);
print "$xmlString\n";
return $xmlString;
}
## wrapper for the recurser,
sub examineHash {
my ( $hash, $xmlDoc, $lastElement ) = @_;
my %refsHash; # hash ref counter, use
+d to prevent/detect circular refs
examineHashRecurse( \%refsHash, $hash, $xmlDoc, $lastElement );
+ # here we go
}
## recurser!
sub examineHashRecurse {
my ( $refsHash, $hash, $xmlDoc, $lastElement ) = @_;
foreach my $key ( sort { $a cmp $b } keys( %{$hash} ) ) {
+ # go through keys at current level
if ( ( $$hash{$key} . "" ) =~ /HASH\(/ ) {
+ # is it another hash?
## its another hash, go deeper!
if ( !exists $$refsHash{ $$hash{$key} } ) {
+ # check if its in the reference hash
$$refsHash{ $$hash{$key} } = 1;
# print "create element $key\n";
my $newElement = $$xmlDoc->createElement($key);
+ # create new element
$$lastElement->appendChild($newElement);
+ # add element to our doc under the last element
## remember to pass the new element as a reference.
examineHashRecurse( $refsHash, $$hash{$key}, $xmlDoc,
+\$newElement );
} ## else, this has alread been examined! stops circula
+r continuation
else {
# do something with a circular reference???
$$refsHash{ $$hash{$key} }++;
}
} else {
# print "create attribute $key with value " . $
+$hash{$key} . "\n";
$$lastElement->setAttribute( "$key", $$hash{$key} ); #
+add attribute to the last element!
}
}
}
use strict;
use XML::LibXML;
my $file;
$file = 'myHash2Xml.xml';
## load the XML
my $parser = XML::LibXML->new();
my $tree = $parser->parse_file($file);
my $root = $tree->getDocumentElement;
## get all the elements that exist in a doc. Note the wildcard to sear
+ch through everything.
my @allDocElements = $root->getElementsByTagName('*');
## some stats meters
my $useableElements = 0;
my $useableAtts = 0;
my $count = @allDocElements;
## iterate over all the elements
foreach my $el1 (@allDocElements) {
## if we have no child nodes we are at the bottom of the tree. Thi
+s is what we
## will be looking for most of the time.
if ( !$el1->hasChildNodes() ) {
if ( $el1->hasAttributes() ) {
## iterate over all the attributes
$useableElements++;
foreach my $ttt ( $el1->attributes() ) {
$useableAtts++;
my $string = $el1->nodePath() . "/" . $ttt->localName
+. " = " . $ttt->nodeValue;
print "AT - $string\n";
}
} else {
## haven't hit this yet
$useableElements++;
my $string = $el1->nodePath() . "/--" . $el1->localName .
+" = " . $el1->nodeValue;
print "EL - $string\n";
}
} else {
## if we are not at the bottom of the tree, we could still hav
+e attributes. Check here
## if we do and process as above
if ( $el1->hasAttributes() ) {
$useableElements++;
foreach my $ttt ( $el1->attributes() ) {
$useableAtts++;
my $string = $el1->nodePath() . "/" . $ttt->localName
+. " = " . $ttt->nodeValue;
print "AT - $string\n";
}
} else {
## keep an eye out for text added to elements
if ( $el1->textContent() !~ /\n/ ) {
$useableElements++;
my $string = $el1->nodePath() . " = " . $el1->textCont
+ent;
print "TX - $string\n";
}
}
}
}
print "\nFrom $count we found $useableElements useable elements, with
+$useableAtts attributes\n";