Getting at XML tree data in perl - perl

I need to parse an XML file using perl which I can load the file using the XML::Simple module but within the XML tree there is a tag that I can't see using the DataDumper module but I can see it's value instead.
<testcase id="10">
.
.
.
</testcase>
Above is a Sample of the XML file with the testcase tag. It's the part that I have difficulty with. Using DataDumper to view the contents of the array I see something like this:
$VAR1 = {
'testcases' => {
'file' => 'testcases.xml',
'testcase' => {
'10' => {
},
Since the XML is defined like why isn't it layed out in the VAR1 array with the id included? Instead of expecting testcases->testcase->id I get testcases->testcase->10. Which 10 is the id but what happened to the 'id' tag?

That's because the default config includes
KeyAttr => [qw( name key id )]
Specifying
KeyAttr => []
will cause id to be no different than any other attribute.

Related

Dancer2: fetching nested data from hash stored in YAML session

I'm using Dancer2 and the YAML session engine. I stored a complete hash in a session with the following code:
post '/login' => sub {
# ...
my $userdata = {
fname => 'John',
lname => 'Doe',
uid => 1234,
};
# ...
session userdata => $userdata;
# ...
}
The omitted code checks the login data against a database and returns that $userdata hashref.
This code creates a session file under $appdir/sessions with this content:
session file
userdata:
fname: John
lname: Doe
uid: 1234
How can I retrieve single values from this session in my app.pm file?
It works great in the template files (*.tt) and <% session.userdata.fname %> yields John, as expected.
However, I want to fetch the first name in app.pm, like so:
get '/userdetails' => sub {
my $firstname = session('userdata.fname'); # gives undef
# do sth. with $firstname
}
Is that feasable? Or do I have to
my $userdata = session('userdata'); # fetch complete hash
# do sth. with $userdata->{fname}
I tried
session('userdata.fname')
session('userdata/fname')
session('userdata:fname')
session('userdata fname')
RTFM (YAML's and Dancer2's)
but none of them worked and gave undef. The manuals and tutorials only fetch "first level values", not nested ones.
The Template Toolkit syntax is completely independent of Perl Dancer2, and you should expect any form of addressing to carry over. The origin of the data as a YAML file is also irrelevant, as the Dancer session is just a Perl hash structure
The manual doesn't make it very clear, but
session('userdata')
is the same as
session->{userdata}
So you can use
session->{userdata}{fname}
to read the subsidiary fields
(Or possibly session('username')->{fname} if you prefer, but that looks a bit icky to me!)
Note that you shouldn't use the YAML session engine in production code, as it's very slow

How to build a hashref with arrays in perl?

I am having trouble building what i think is a hashref (href) in perl with XML::Simple.
I am new to this so not sure how to go about it and i cant find much to build this href with arrays. All the examples i have found are for normal href.
The code bellow outputs the right xml bit, but i am really struggling on how to add more to this href
Thanks
Dario
use XML::Simple;
$test = {
book => [
{
'name' => ['createdDate'],
'value' => [20141205]
},
{
'name' => ['deletionDate'],
'value' => [20111205]
},
]
};
$test ->{book=> [{'name'=> ['name'],'value'=>['Lord of the rings']}]};
print XMLout($test,RootName=>'library');
To add a new hash to the arrary-ref 'books', you need to cast the array-ref to an array and then push on to it. #{ $test->{book} } casts the array-ref into an array.
push #{ $test->{book} }, { name => ['name'], value => ['The Hobbit'] };
XML::Simple is a pain because you're never sure whether you need an array or a hash, and it is hard to distinguish between elements and attributes.
I suggest you make a move to XML::API. This program demonstrates some how it would be used to create the same XML data as your own program that uses XML::Simple.
It has an advantage because it builds a data structure in memory that properly represents the XML. Data can be added linearly, like this, or you can store bookmarks within the structure and go back and add information to nodes created previously.
This code adds the two book elements in different ways. The first is the standard way, where the element is opened, the name and value elements are added, and the book element is closed again. The second shows the _ast (abstract syntax tree) method that allows you to pass data in nested arrays similar to those in XML::Simple for conciseness. This structure requires you to prefix attribute names with a hyphen - to distinguish them from element names.
use strict;
use warnings;
use XML::API;
my $xml = XML::API->new;
$xml->library_open;
$xml->book_open;
$xml->name('createdDate');
$xml->value('20141205');
$xml->book_close;
$xml->_ast(book => [
name => 'deletionDate',
value => '20111205',
]);
$xml->library_close;
print $xml;
output
<?xml version="1.0" encoding="UTF-8" ?>
<library>
<book>
<name>createdDate</name>
<value>20141205</value>
</book>
<book>
<name>deletionDate</name>
<value>20111205</value>
</book>
</library>

The xml attribute "id" seems to be a protected attribute. What config needed to be able to set it?

I am using the perl module XML::Simple to create an XML structure.
Everything works fine except that the string "<tag1 id="5"> ABC </tag1>"
looks like <tag1 name="5"> ABC </tag1> afterwards.
I use the string with XML::Simple as follows
my $simple = XML::Simple->new();
my $tree = $simple->XMLin($my_xml_string, ForceArray => 1);
$resp->data()->{'xml'} = $tree;
The xml attribute id seems to be a protected attribute, because i get name in the output .
What config is needed to be able to set it?
Look at the KeyAttr option. You probably want KeyAttr => [] to de-activate array to hash folding

What is wrong with my declaration of a hash inside a hash in Perl?

I am struggling with the following declaration of a hash in Perl:
my %xmlStructure = {
hostname => $dbHost,
username => $dbUsername,
password => $dbPassword,
dev_table => $dbTable,
octopus => {
alert_dir => $alert_dir,
broadcast_id => $broadcast_id,
system_id => $system_id,
subkey => $subkey
}
};
I've been googling, but I haven't been able to come up with a solution, and every modification I make ends up in another warning or in results that I do not want.
Perl complaints with the following text:
Reference found where even-sized list expected at ./configurator.pl line X.
I am doing it that way, since I want to use the module:
XML::Simple
In order to generate a XML file with the following structure:
<settings>
<username></username>
<password></password>
<database></database>
<hostname></hostname>
<dev_table></dev_table>
<octopus>
<alert_dir></alert_dir>
<broadcast_id></broadcast_id>
<subkey></subkey>
</octopus>
</settings>
so sometthing like:
my $data = $xmlFile->XMLout(%xmlStructure);
warn Dumper($data);
would display the latter xml sample structure.
Update:
I forgot to mention that I also tried using parenthesis instead of curly braces for the hash reference, and eventhough it seems to work, the XML file is not written properly:
I end up with the following structure:
<settings>
<dev_table>5L3IQWmNOw==</dev_table>
<hostname>gQMgO3/hvMjc</hostname>
<octopus>
<alert_dir>l</alert_dir>
<broadcast_id>l</broadcast_id>
<subkey>l</subkey>
<system_id>l</system_id>
</octopus>
<password>dZJomteHXg==</password>
<username>sjfPIQ==</username>
</settings>
Which is not exactly wrong, but I'm not sure if I'm going to have problems latter on as the XML file grows bigger. The credentials are encrypted using RC4 algorith, but I am encoding in base 64 to avoid any misbehavior with special characters.
Thanks
{} are used for hash references. To declare a hash use normal parentheses ():
my %xmlStructure = (
hostname => $dbHost,
username => $dbUsername,
password => $dbPassword,
dev_table => $dbTable,
octopus => {
alert_dir => $alert_dir,
broadcast_id => $broadcast_id,
system_id => $system_id,
subkey => $subkey
}
);
See also perldoc perldsc - Perl Data Structures Cookbook.
For your second issue, you should keep in mind that XML::Simple is indeed too simple for most applications. If you need a specific layout, you're better off with a different way of producing the XML, say, using HTML::Template. For example (I quoted variable names for illustrative purposes):
#!/usr/bin/env perl
use strict; use warnings;
use HTML::Template;
my $tmpl = HTML::Template->new(filehandle => \*DATA);
$tmpl->param(
hostname => '$dbHost',
username => '$dbUsername',
password => '$dbPassword',
dev_table => '$dbTable',
octopus => [
{
alert_dir => '$alert_dir',
broadcast_id => '$broadcast_id',
system_id => '$system_id',
subkey => '$subkey',
}
]
);
print $tmpl->output;
__DATA__
<settings>
<username><TMPL_VAR username></username>
<password><TMPL_VAR password></password>
<database><TMPL_VAR database></database>
<hostname><TMPL_VAR hostname></hostname>
<dev_table><TMPL_VAR dev_table></dev_table>
<octopus><TMPL_LOOP octopus>
<alert_dir><TMPL_VAR alert_dir></alert_dir>
<broadcast_id><TMPL_VAR broadcast_id></broadcast_id>
<subkey><TMPL_VAR subkey></subkey>
<system_id><TMPL_VAR system_id></system_id>
</TMPL_LOOP></octopus>
</settings>
Output:
<settings>
<username>$dbUsername</username>
<password>$dbPassword</password>
<database></database>
<hostname>$dbHost</hostname>
<dev_table>$dbTable</dev_table>
<octopus>
<alert_dir>$alert_dir</alert_dir>
<broadcast_id>$broadcast_id</broadcast_id>
<subkey>$subkey</subkey>
<system_id>$system_id</system_id>
</octopus>
</settings>
You're using the curly braces { ... } to construct a reference to an anonymous hash. You should either assign that to a scalar, or change the { ... } to standard parentheses ( ... ).

Example Perl code for generating XML from XSD using XML::Compile

Can anybody please show me an example for generating XML from XSD using XML::Compile::Schema.
I am trying to post my script which I am trying along with the XSD but I am not able to do that. so I am looking for a any sample example.
I wrote a tutorial on this a while ago: http://blogs.perl.org/users/brian_e_lozier/2011/10/using-xmlcompile-to-output-xsd-compliant-xml.html
In short words, you'll need to do:
Convert the XSD format to Perl hash structure
Construct this Hash, fill in the data
Convert the Hash to XML
Packages required:
XML::Compile::Schema
XML::LibXML::Document
Following code create a Perl structure from XSD definition.
use XML::Compile::Schema;
use Data::Dumper;
my $filename = $ARGV[0] || "";
if(!$filename){
warn "Please provide the WSDL definition file.";
exit 10;
}
my $schema = XML::Compile::Schema->new($filename);
my $hash;
print Dumper $schema->template('PERL' => 'Application');
Then the Perl data structure created by this program looks like:
{
MakeName =>
{
UniqueID => "anything",
_ => "example", },
MakeDetails =>
{
Name =>
{
UniqueID => "anything",
_ => "example", },
},
};
So the rest of your job will create the same structure in your program, fill in the content like:
my $hash = {
MakeName => {
UniqueID => 'xxxx',
_ => 'Name of the Make',
},
OtherFields => foo_bar_get_other_hash(),
};
....
## breathtaking moment, create the XML from this $hash
my $schema = XML::Compile::Schema->new("/opt/data/your.xsd");
my $doc = XML::LibXML::Document->new();
my $writer = $schema->compile(WRITER => 'Application');
my $xml;
## Create $xml in the memory based on the Schema and your $hash
eval{ $xml = $writer->($doc, $hash);};
if($#){
# Useful if the format is invalid against the Schema definition
# Or if there are other errors may occurs
$err_msg = $#->{message}->toString();
return ("", $err_msg);
}
## If you want save this $xml to file, convert it to string format first
$doc->setDocumentElement($xml);
my $ori_content = $doc->toString(1);
## Now $ori_content holds the full XML content.