I'm sending a fairly simple Perl hash to SOAP::Data, but I'm not getting the XML that I want with an array of hashes. Here's what I'm sending it:
'hash' => {
'Location' => [
{
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
},
{
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
},
],
}
Here's what I get:
<hash>
<Location>
<c-gensym9>
<key1>value1</key1>
<key2>value2</key2>
<key3>value3</key3>
</c-gensym9>
<c-gensym10>
<key1>value1</key1>
<key2>value2</key2>
<key3>value3</key3>
</c-gensym10>
</Location>
</hash>
But what I want is this:
<hash>
<Location>
<key1>value1</key1>
<key2>value2</key2>
<key3>value3</key3>
</Location>
<Location>
<key1>value1</key1>
<key2>value2</key2>
<key3>value3</key3>
</Location>
</hash>
What am I missing? I suppose it'd help if I gave some code!:
my $hash = {};
my #Locations;
my #loc_codes = qw(0_4_10 0_51_117);
foreach my $l ( #loc_codes ) {
my #arr = split ('_', $l);
my $loc = {};
$loc->{key1} = $arr[0]; # country
$loc->{key2} = $arr[1]; # state
$loc->{key3} = $arr[2]; # city
push ( #Locations, $loc );
}
$hash->{Location} = \#Locations;
my $soap_elements = SOAP::Data->value(
SOAP::Data->name( 'some_method' => $hash )->prefix('p1')
)->prefix('p2');
You need strict, first of all
use strict;
use SOAP::Lite +trace => 'all';
my #loc_codes = qw(0_4_10 0_51_117);
my #Locations;
foreach my $l ( #loc_codes ) {
my #arr = split ('_', $l);
my $loc =
SOAP::Data
->name("Location" => \SOAP::Data->value(
SOAP::Data->name('key1', $arr[0]),
SOAP::Data->name('key2', $arr[1]),
SOAP::Data->name('key3', $arr[2])));
push ( #Locations, $loc );
}
my $soap_elements;
$soap_elements = SOAP::Data->value(
SOAP::Data->name( hash => \#Locations ));
my $serializer = SOAP::Serializer->new();
$serializer->readable('true');
my $xml = $serializer->serialize($soap_elements);
print $xml;
generates
<hash
soapenc:arrayType="xsd:anyType[2]"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="soapenc:Array">
<Location>
<key1 xsi:type="xsd:int">0</key1>
<key2 xsi:type="xsd:int">4</key2>
<key3 xsi:type="xsd:int">10</key3>
</Location>
<Location>
<key1 xsi:type="xsd:int">0</key1>
<key2 xsi:type="xsd:int">51</key2>
<key3 xsi:type="xsd:int">117</key3>
</Location>
</hash>
So I think you need to build your array elements first
Well ,currently , SOAP::Data is not as powerful as you imagined. It cannot dynamically or recursively analyse your compound(complex) data structure such as the nest of array , hash and scalar value.You can just pass simple data(scalar value)to SOAP::Data ,or , an SOAP::Data instance which is already assembled manually by your self thus creating a nested SOAP::Data strucure;Please refer to the SOAP::Data tutorial.
The tutorial has a simple but clear explanation on how to use SOAP::Data to deal with complex data type . But personally ,I don't recommend you to use perl as a soap client .From my previous experience, perl don't have a full support for SOAP protocal ,especially when dealing with complex data type or higher-version soap like soap 1.2.. Instead,you'd better use java .java has a very power support for latest soap and complex data type.
Related
In this code, I have two different tables i.e. skill_master and jobs_category. Now, I want to get these two different table data into one and also convert its data into JSON format using json_encode.
$this->db->select('category');
$this->db->from('jobs_category');
$this->db->order_by('category');
$query1 = $this->db->get();
$result1 = $query1->result_array();
$this->db->select('key_skills');
$this->db->from('skill_master');
$this->db->order_by('key_skills');
$query2 = $this->db->get();
$result2 =$query2->result_array();
$arr = array();
foreach($result1 as $row)
{
foreach($result2 as $rows)
{
$arr[] = $row['category'].','.$rows['skill_master'];
}
}
$json = json_encode($arr);
echo $json;
For example:
table1: skill_master
key_skills
==========
java
php
dot net
table2: jobs_category
category
========
IT Jobs
Air line Jobs
Hardware Jobs
Now, Here I have two tables here. Now, I want to combine these two tables and want data in JSON format like ["java", "PHP", "dot net", "IT Jobs", "Air Line Jobs", "Hardware Jobs"]. So, How can I do this? Please help me.
Thank You
$this->db->select('category');
$this->db->from('jobs_category');
$this->db->order_by('category');
$query_category= $this->db->get();
$result_category = $query_category->result_array();
$this->db->select('key_skills');
$this->db->from('skill_master');
$this->db->order_by('key_skills');
$query_skills = $this->db->get();
$result_skills =$query_skills->result_array();
If You get records from table jobs_category and skill_master like this
$result_category =
[
'0' => ['category' => 'IT Jobs'],
'1' => ['category' => 'Air line Jobs'],
'2' => ['category' => 'Hardware Jobs']
];
$result_skills =
[
'0' => ['skill_master' => 'java'],
'1' => ['skill_master' => 'php'],
'2' => ['skill_master' => 'dot net']
];
$final_arr = $final_category_arr = $final_skill_arr = [];
foreach($result_category as $category_row)
{
$final_category_arr[] = $category_row['category'];
}
foreach($result_skills as $skill_row)
{
$final_skill_arr[] = $skill_row['skill_master'];
}
$final_arr = array_merge($final_category_arr, $final_skill_arr);
$json = json_encode($final_arr);
echo $json;
Result will be like this
["IT Jobs","Air line Jobs","Hardware Jobs","java","php","dot net"]
renaming column while fetching and merging data should work. try following code
$this->db->select('category');
$this->db->from('jobs_category');
$this->db->order_by('category');
$query_category= $this->db->get();
$result_category = $query_category->result_array();
$this->db->select('key_skills as category');
$this->db->from('skill_master');
$this->db->order_by('key_skills');
$query_skills = $this->db->get();
$result_skills =$query_skills->result_array();
$result = array_merge($result_category,$result_skills);
I really dont know how to do it so I ended up here.
I want to convert this input:
my #sack_files_1 = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat',
'u5env/1_2_3/parent.dx_environment',
'u5env/1_2_3/doc/types.dat',
);
To this:
my $sack_tree_1 = {
'mgenv' => {
'1_2_3' => [ 'parent.dx_environment', 'doc/types.dat' ],
},
'u5env' => {
'1_2_3' => [ 'parent.dx_environment', 'doc/types.dat' ],
}
};
Something like this should do the trick:
use strict;
use warnings;
use Data::Dumper;
my #sack_files_1 = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat',
'u5env/1_2_3/parent.dx_environment',
'u5env/1_2_3/doc/types.dat',
);
my %sack_tree_1;
foreach (#sack_files_1) {
my ( $env, $number, #everything_else ) = split('/');
push( #{ $sack_tree_1{$env}{$number} }, join( "/", #everything_else ) );
}
print Dumper \%sack_tree_1
This will do as you ask. It uses File::Spec::Functions to split each path into its components.
The first two elements of the hash are used directly as hash keys, relying on autovivication to create the necessary hash elements.
A simple push to an implied array reference also autovivifies the lowest-level hash element.
I have used Data::Dump to display the resulting hash. It is not part of the core Perl installation and you may need to install it, but it is much superior to Data::Dumper.
use strict;
use warnings;
use File::Spec::Functions qw/ splitdir catfile /;
my #sack_files_1 = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat',
'u5env/1_2_3/parent.dx_environment',
'u5env/1_2_3/doc/types.dat',
);
my %paths;
for my $path (#sack_files_1) {
my ($p1, $p2, #path) = splitdir $path;
push #{ $paths{$p1}{$p2} }, catfile #path;
}
use Data::Dump;
dd \%paths;
output
{
mgenv => { "1_2_3" => ["parent.dx_environment", "doc\\types.dat"] },
u5env => { "1_2_3" => ["parent.dx_environment", "doc\\types.dat"] },
}
my $sack_tree_1 = {};
foreach my $data (#sack_files_1) {
my #path = split '/', $data;
my ($file,$last_part) = pop #path, pop #path; # get the file name and last part of the path
my $hash_part = $sack_tree_1;
foreach my $path (#path) { # For every element in the remaining part of the path
$hash_part->{$path} //= {}; # Make sure we have a hash ref to play with
$hash_part = $hash_part->{$path} # Move down the hash past the current path element
}
$hash_part->{$last_part} = $file; # Add the file name to the last part of the path
}
This handles all path lengths of 2 or more
GENERAL IDEA
Here is a snippet of what I'm working with:
my $url_temp;
my $page_temp;
my $p_temp;
my #temp_stuff;
my #collector;
foreach (#blarg_links) {
$url_temp = $_;
$page_temp = get( $url_temp ) or die $!;
$p_temp = HTML::TreeBuilder->new_from_content( $page_temp );
#temp_stuff = $p_temp->look_down(
_tag => 'foo',
class => 'bar'
);
foreach (#temp_stuff) {
push(#collector, "http://www.foobar.sx" . $1) if $_->as_HTML =~ m/href="(.*?)"/;
};
};
Hopefully it is clear that what I'm hopelessly trying to do is push the link endings found in each of a list of links into an array called #temp_stuff. So the first link in #blarg_links, when visited, has greater than or equal to 1 foo tag with an associated bar class that when acted on by as_HTML will match something I want in the href equality to then pump into an array of links which have the data I'm really after... Does that make sense?
ACTUAL DATA
my $url2 = 'http://www.chemistry.ucla.edu/calendar-node-field-date/year';
my $page2 = get( $url2 ) or die $!;
my $p2 = HTML::TreeBuilder->new_from_content( $page2 );
my #stuff2 = $p2->look_down(
_tag => 'div',
class => 'year mini-day-on'
);
my #chem_links;
foreach (#stuff2) {
push(#chem_links, $1) if $_->as_HTML =~ m/(http:\/\/www\.chemistry\.ucla\.edu\/calendar-node-field-date\/day\/[0-9]{4}-[0-9]{2}-[0-9]{2})/;
};
my $url_temp;
my $page_temp;
my $p_temp;
my #temp_stuff;
my #collector;
foreach (#chem_links) {
$url_temp = $_;
$page_temp = get( $url_temp ) or die $!;
$p_temp = HTML::TreeBuilder->new_from_content( $page_temp );
#temp_stuff = $p_temp->look_down(
_tag => 'span',
class => 'field-content'
);
};
foreach (#temp_stuff) {
push(#collector, "http://www.chemistry.ucla.edu" . $1) if $_->as_HTML =~ m/href="(.*?)"/;
};
n.b. - I want to use HTML::TreeBuilder. I'm aware of alternatives.
This is a rough attempt at what I think you want.
It fetches all the links on the first page and visits each of them in turn, printing the link in each <span class="field-content"> element.
use strict;
use warnings;
use 5.010;
use HTML::TreeBuilder;
STDOUT->autoflush;
my $url = 'http://www.chemistry.ucla.edu/calendar-node-field-date/year';
my $tree = HTML::TreeBuilder->new_from_url($url);
my #chem_links;
for my $div ( $tree->look_down( _tag => 'div', class => qr{\bmini-day-on\b} ) ) {
my ($anchor)= $div->look_down(_tag => 'a', href => qr{http://www\.chemistry\.ucla\.edu});
push #chem_links, $anchor->attr('href');
};
my #collector;
for my $url (#chem_links) {
say $url;
my $tree = HTML::TreeBuilder->new_from_url($url);
my #seminars;
for my $span ( $tree->look_down( _tag => 'span', class => 'field-content' ) ) {
my ($anchor) = $span->look_down(_tag => 'a', href => qr{/});
push #seminars, 'http://www.chemistry.ucla.edu'.$anchor->attr('href');
}
say " $_" for #seminars;
say '';
push #collector, #seminars;
};
For a more modern framework for parsing webpages, I would suggest you take a look at Mojo::UserAgent and Mojo::DOM. Instead of having to manually march through each section of your html tree, you can use the power of css selectors to zero in on the specific data that you want. There's a nice 8 minute introductory video on the framework at Mojocast Episode 5.
# Parses the UCLA Chemistry Calendar and displays all seminar links
use strict;
use warnings;
use Mojo::UserAgent;
use URI;
my $url = 'http://www.chemistry.ucla.edu/calendar-node-field-date/year';
my $ua = Mojo::UserAgent->new;
my $dom = $ua->get($url)->res->dom;
for my $dayhref ($dom->find('div.mini-day-on > a[href*="/day/"]')->attr('href')->each) {
my $dayurl = URI->new($dayhref)->abs($url);
print $dayurl, "\n";
my $daydom = $ua->get($dayurl->as_string)->res->dom;
for my $seminarhref ($daydom->find('span.field-content > a[href]')->attr('href')->each) {
my $seminarurl = URI->new($seminarhref)->abs($dayurl);
print " $seminarurl\n";
}
print "\n";
}
Output is identical to that of Borodin's solution using HTML::TreeBuilder:
http://www.chemistry.ucla.edu/calendar-node-field-date/day/2014-01-06
http://www.chemistry.ucla.edu/seminars/nano-rheology-enzymes
http://www.chemistry.ucla.edu/calendar-node-field-date/day/2014-01-09
http://www.chemistry.ucla.edu/seminars/imaging-approach-biology-disease-through-chemistry
http://www.chemistry.ucla.edu/calendar-node-field-date/day/2014-01-10
http://www.chemistry.ucla.edu/seminars/arginine-methylation-%E2%80%93-substrates-binders-function
http://www.chemistry.ucla.edu/seminars/special-inorganic-chemistry-seminar
http://www.chemistry.ucla.edu/calendar-node-field-date/day/2014-01-13
http://www.chemistry.ucla.edu/events/robert-l-scott-lecture-0
...
I have written the code below in Perl but it's not giving the desirable output. I am dealing with the comparison between one array and two hash of arrays.
Given sample input files:
1) file1.txt
A6416 A2318
A84665 A88
2) hashone.pl
%hash1=(
A6416=>['E65559', 'C11162.1', 'c002gnj.3',],
A88=>['E77522', 'M001103', 'C1613.1', 'c001hyf.2',],
A84665=>['E138347', 'M032578', 'C7275.1', 'c009xpt.3',],
A2318=>['E128591', 'C43644.1', 'C47705.1', 'c003vnz.4',],
);
3) hashtwo.pl
%hash2=(
15580=>['C7275.1', 'E138347', 'M032578', 'c001jnm.3', 'c009xpt.2'],
3178=>['C1613.1', 'E77522','M001103', 'c001hyf.2', 'c001hyg.2'],
24406=>['C11162.1', 'E65559', 'M003010', 'c002gnj.2'],
12352=>['C43644.1', 'C47705.1', 'E128591','M001458', 'c003vnz.3'],
);
My aim is to achieve the task described:
From file1.txt, I have to locate the corresponding ID in %hash1. For instance,A6416 (file1.txt) is the key in %hash1. Next, I have to find the values of A6416 ['E65559', 'C11162.1', 'c002gnj.3',] in %hash2. If majority (more than 50%) of the values are found in %hash2, I replace A6416 with corresponding key from %hash2.
Example:
A6416 A2318
A84665 A88
Output:
24406 12352
15580 3178
Please note that the keys for %hash1 and %hash2 are different (they don't overlap). But the values are the same (they overlap).
#!/usr/bin/perl -w
use strict;
use warnings;
open FH, "file1.txt" || die "Error\n";
my %hash1 = do 'hashone.pl';
my %hash2 = do 'hashtwo.pl';
chomp(my #array=<FH>);
foreach my $amp (#array)
{
if ($amp =~ /(\d+)(\s?)/)
{
if (exists ($hash1{$1}))
{
for my $key (keys %hash2)
{
for my $i ( 0 .. $#{ $hash2{$key} } )
{
if ((#{$hash1{$1}}) eq ($hash2{$key}[$i]))
{
print "$key";
}
}
}
}
}
}
close FH;
1;
Any guidance on this problem is highly appreciated. Thank you!
I think you should invert %hash2 into this structure:
$hash2{'C7275.1'} = $hash2{'E138347'} = $hash2{'M032578'}
= $hash2{'c001jnm.3'} = $hash2{'c009xpt.2'} = 15580;
$hash2{'C1613.1'} = $hash2{'E77522'} = $hash2{'M001103'}
= $hash2{'c001hyf.2'} = $hash2{'c001hyg.2'} = 3178;
$hash2{'C11162.1'} = $hash2{'E65559'}
= $hash2{'M003010'} = $hash2{'c002gnj.2'} = 24406;
$hash2{'C43644.1'} = $hash2{'C47705.1'} = $hash2{'E128591'}
= $hash2{'M001458'} = $hash2{'c003vnz.3'} = 3178;
So that you can perform these look-ups much more effectively, rather than having to iterate over every element of every element of %hash2.
Building on the responses from ruakh and zock here is the code you need to build the look-up table for hash2
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %hash2=(
15580=>['C7275.1', 'E138347', 'M032578', 'c001jnm.3', 'c009xpt.2'],
3178=>['C1613.1', 'E77522','M001103', 'c001hyf.2', 'c001hyg.2'],
24406=>['C11162.1', 'E65559', 'M003010', 'c002gnj.2'],
12352=>['C43644.1', 'C47705.1', 'E128591','M001458', 'c003vnz.3'],
);
# Build LUT for hash2
my %hash2_lut;
foreach my $key (keys %hash2)
{
foreach my $val (#{$hash2{$key}})
{
$hash2_lut{$val} = $key
}
}
print Dumper(\%hash2_lut);
Please select ruakh's post as the answer, just trying to clarify the code for you. Use Data::Dumper...it is your friend.
Here is the output:
$VAR1 = {
'C47705.1' => '12352',
'M032578' => '15580',
'E138347' => '15580',
'E77522' => '3178',
'C7275.1' => '15580',
'c001jnm.3' => '15580',
'E65559' => '24406',
'C1613.1' => '3178',
'M001458' => '12352',
'c002gnj.2' => '24406',
'c009xpt.2' => '15580',
'c001hyf.2' => '3178',
'C43644.1' => '12352',
'E128591' => '12352',
'c001hyg.2' => '3178',
'M003010' => '24406',
'c003vnz.3' => '12352',
'C11162.1' => '24406',
'M001103' => '3178'
};
I am using xpath to retrieve the text value of some links (a) in a given element. I then push the results to an array called $tableau. Everything is working fine :) The thing is, I would like now to retrieve also the href of those links. So have both the text value and the href in my array. The other thing is, I have no idea how to do that. Hope someone can help. Thank you in advance for your replies. Cheers. Marc
$url = 'http://www.somesite.com';
$path = '.../a';
$tableau = array();
$dom = new DOMDocument();
#$dom->loadHTML($url);
$xpath = new DomXPath($dom);
$x = $xpath->query($path);
foreach ($x as $value)
array_push($tableau, $value->nodeValue);
Try this:
foreach ($x as $value)
{
array_push($tableau, $value->nodeValue, $value->getAttribute('href'));
}
you can use an associative array
the code might look like this(not sure abt the syntax though)
arr = [ "1" => [ "text" => "value1" , "href" => "value1"],
"2" => [ "text" => "value2", "href" => "value2"]];
the xpath xpression to retrieve the href of all the anchor tags is something like this
("//a/#href");
// --> select a from all the descendants of the root
a --> select the anchor tag
#href --> select the href of this anchor (# to select attributes)
convert this expression to equivalent PHP code
Thanks to the hints given by javram and vireshad I manage to find the solution. See here below:
$url = 'http://www.somesite.com';
$path = '.../a';
$tableau = array();
$dom = new DOMDocument();
#$dom->loadHTML($url);
$xpath = new DomXPath($dom);
$x = $xpath->query($path);
foreach ($x as $value)
array_push($tableau, array($value->nodeValue, $value->getAttribute('href')));