Processing results from fatfree DB SQL Mapper for json encoding - json

I'm having trouble processing the returned results from a DB SQL Mapper into a recognizable json encoded array.
function apiCheckSupplyId() {
/*refer to the model Xrefs*/
$supply_id = $this->f3->get('GET.supply_id');
$xref = new Xrefs($this->tongpodb);
$supply = $xref->getBySupplyId( $supply_id );
if ( count( $supply ) == 0 ) {
$this->logger->write('no xref found for supply_id=' .$supply_id);
$supply = array( array('id'=>0) );
echo json_encode( $supply );
} else {
$json = array();
foreach ($supply as $row){
$item = array();
foreach($row as $key => $value){
$item[$key] = $value;
}
array_push($json, $item);
}
$this->logger->write('xref found for supply_id=' .$supply_id.json_encode( $json ) );
echo json_encode( $json );
}
}
This is the method I am using but it seems very clunky to me. Is there a better way?

Assuming the getBySupplyId returns an array of Xref mappers, you could simplify the whole thing like this:
function apiCheckSupplyId() {
$supply_id = $this->f3->get('GET.supply_id');
$xref = new Xrefs($this->tongpodb);
$xrefs = $xref->getBySupplyId($supply_id);
echo json_encode(array_map([$xref,'cast'],$xrefs));
$this->logger->write(sprintf('%d xrefs found for supply_id=%d',count($xrefs),$supply_id));
}
Explanation:
The $xrefs variable contains an array of mappers. Each mapper being an object, you have to cast it to an array before encoding it to JSON. This can be done in one line by mapping the $xref->cast() method to each record: array_map([$xref,'cast'],$xrefs).
If you're not confident with that syntax, you can loop through each record and cast it:
$cast=[];
foreach ($xrefs as $x)
$cast[]=$x->cast();
echo json_encode($cast);
The result is the same.
The advantage of using cast() other just reading each value (as you're doing in your original script) is that it includes virtual fields as well.

Related

DBI convert fetched arrayref to hash

I'm trying to write a program to fetch a big MySQL table, rename some fields and write it to JSON. Here is what I have for now:
use strict;
use JSON;
use DBI;
# here goes some statement preparations and db initialization
my $rowcache;
my $max_rows = 1000;
my $LIMIT_PER_FILE = 100000;
while ( my $res = shift( #$rowcache )
|| shift( #{ $rowcache = $sth->fetchall_arrayref( undef, $max_rows ) } ) ) {
if ( $cnt % $LIMIT_PER_FILE == 0 ) {
if ( $f ) {
print "CLOSE $fname\n";
close $f;
}
$filenum++;
$fname = "$BASEDIR/export-$filenum.json";
print "OPEN $fname\n";
open $f, ">$fname";
}
$res->{some_field} = $res->{another_field}
delete $res->{another_field}
print $f $json->encode( $res ) . "\n";
$cnt++;
}
I used the database row caching technique from
Speeding up the DBI
and everything seems good.
The only problem I have for now is that on $res->{some_field} = $res->{another_field}, the row interpreter complains and says that $res is Not a HASH reference.
Please could anybody point me to my mistakes?
If you want fetchall_arrayref to return an array of hashrefs, the first parameter should be a hashref. Otherwise, an array of arrayrefs is returned resulting in the "Not a HASH reference" error. So in order to return full rows as hashref, simply pass an empty hash:
$rowcache = $sth->fetchall_arrayref({}, $max_rows)

Perl XML2JSON : How to preserve XML element order?

I have a configuration file which is in XML format. I need to parse the XML and convert to JSON. I'm able to convert it with XML2JSON module of perl. But the problem is, it is not maintaining the order of XML elements. I strictly need the elements in order otherwise I cannot configure
My XML file is something like this. I have to configure an IP address and set that IP as a gateway to certain route.
<Config>
<ip>
<address>1.1.1.1</address>
<netmask>255.255.255.0</netmask>
</ip>
<route>
<network>20.20.20.0</network>
<netmask>55.255.255.0</netmask>
<gateway>1.1.1.1</gateway>
</route>
</Config>
This is my perl code to convert to JSON
my $file = 'config.xml';
use Data::Dumper;
open my $fh, '<',$file or die;
$/ = undef;
my $data = <$fh>;
my $XML = $data;
my $XML2JSON = XML::XML2JSON->new();
my $Obj = $XML2JSON->xml2obj($XML);
print Dumper($Obj);
The output I'm getting is,
$VAR1 = {'Config' => {'route' => {'netmask' => {'$t' => '55.255.255.0'},'gateway' => {'$t' => '1.1.1.1'},'network' => {'$t' => '20.20.20.0'}},'ip' => {'netmask' => {'$t' => '255.255.255.0'},'address' => {'$t' => '1.1.1.1'}}},'#encoding' => 'UTF-8','#version' => '1.0'};
I have a script which reads the json object and configure..
But it fails as it first tries to set gateway ip address to a route where the ip address is not yet configured and add then add ip address.
I strictly want key ip to come first and then route for proper configuration without error. Like this I have many dependencies where order of keys is a must.
Is there any way I can tackle this problem? I tried almost all modules of XML parsing like XML::Simple,Twig::XML,XML::Parser. But nothing helped..
Here's a program that I hacked together that uses XML::Parser to parse some XML data and generate the equivalent JSON in the same order. It ignores any attributes, processing instructions etc. and requires that every XML element must contain either a list of child elements or a text node. Mixing text and elements won't work, and this isn't checked except that the program will die trying to dereference a string
It's intended to be a framework for you to enhance as you require, but works fine as it stands with the XML data you show in your question
use strict;
use warnings 'all';
use XML::Parser;
my $parser = XML::Parser->new(Handlers => {
Start => \&start_tag,
End => \&end_tag,
Char => \&text,
});
my $struct;
my #stack;
$parser->parsefile('config.xml');
print_json($struct->[1]);
sub start_tag {
my $expat = shift;
my ($tag, %attr) = #_;
my $elem = [ $tag => [] ];
if ( $struct ) {
my $content = $stack[-1][1];
push #{ $content }, $elem;
}
else {
$struct = $elem;
}
push #stack, $elem;
}
sub end_tag {
my $expat = shift;
my ($elem) = #_;
die "$elem <=> $stack[-1][0]" unless $stack[-1][0] eq $elem;
for my $content ( $stack[-1][1] ) {
$content = "#$content" unless grep ref, #$content;
}
pop #stack;
}
sub text {
my $expat = shift;
my ($string) = #_;
return unless $string =~ /\S/;
$string =~ s/\A\s+//;
$string =~ s/\s+\z//;
push #{ $stack[-1][1] }, $string;
}
sub print_json {
my ($data, $indent, $comma) = (#_, 0, '');
print "{\n";
for my $i ( 0 .. $#$data ) {
# Note that $data, $indent and $comma are overridden here
# to reflect the inner context
#
my $elem = $data->[$i];
my $comma = $i < $#$data ? ',' : '';
my ($tag, $data) = #$elem;
my $indent = $indent + 1;
printf qq{%s"%s" : }, ' ' x $indent, $tag;
if ( ref $data ) {
print_json($data, $indent, $comma);
}
else {
printf qq{"%s"%s\n}, $data, $comma;
}
}
# $indent and $comma (and $data) are restored here
#
printf "%s}%s\n", ' ' x $indent, $comma;
}
output
{
"ip" : {
"address" : "1.1.1.1",
"netmask" : "255.255.255.0"
},
"route" : {
"network" : "20.20.20.0",
"netmask" : "55.255.255.0",
"gateway" : "1.1.1.1"
}
}
The problem isn't so much to do with XML parsing, but because perl hashes are not ordered. So when you 'write' some JSON... it can be any order.
The way to avoid this is to apply a sort function to your JSON.
You can do this by using sort_by to explicitly sort:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
use JSON::PP;
use Data::Dumper;
sub order_nodes {
my %rank_of = ( ip => 0, route => 1, address => 2, network => 3, netmask => 4, gateway => 5 );
print "$JSON::PP::a <=> $JSON::PP::b\n";
return $rank_of{$JSON::PP::a} <=> $rank_of{$JSON::PP::b};
}
my $twig = XML::Twig -> parse (\*DATA);
my $json = JSON::PP -> new;
$json ->sort_by ( \&order_nodes );
print $json -> encode( $twig -> simplify );
__DATA__
<Config>
<ip>
<address>1.1.1.1</address>
<netmask>255.255.255.0</netmask>
</ip>
<route>
<network>20.20.20.0</network>
<netmask>55.255.255.0</netmask>
<gateway>1.1.1.1</gateway>
</route>
</Config>
In some scenarios, setting canonical can help, as that sets ordering to lexical order. (And means your JSON output would be consistently ordered). This doesn't apply to your case.
You could build the node ordering via XML::Twig, either by an xpath expression, or by using twig_handlers. I gave it a quick go, but got slightly unstuck in figuring out how you'd 'tell' how to figure out ordering based on getting address/netmask and then network/netmask/gateway.
As a simple example you could:
my $count = 0;
foreach my $node ( $twig -> get_xpath ( './*' ) ) {
$rank_of{$node->tag} = $count++ unless $rank_of{$node->tag};
}
print Dumper \%rank_of;
This will ensure ip and route are always the right way around. However it doesn't order the subkeys.
That actually gets a bit more complicated, as you'd need to recurse... and then decide how to handle 'collisions' (like netmask - address comes before, but how does it sort compared to network).
Or alternatively:
my $count = 0;
foreach my $node ( $twig->get_xpath('.//*') ) {
$rank_of{ $node->tag } = $count++ unless $rank_of{ $node->tag };
}
This walks all the nodes, and puts them in order. It doesn't quite work, because netmask appears in both stanzas though.
You get:
{"ip":{"address":"1.1.1.1","netmask":"255.255.255.0"},"route":{"netmask":"55.255.255.0","network":"20.20.20.0","gateway":"1.1.1.1"}}
I couldn't figure out a neat way of collapsing both lists.

Wordpress pods: Exporting specific columns to json

I have the following code for exporting all the items in one of my pods to json. The thing is I don't need all the 130 columns in the json file, but only about 20. Since this will be done for about 150 items I thought I could save some loading time by not printing out all the fields, but I do not know how to do this. For example I only want to print the column value named 'title' for all items in the pod. My code is attached bellow.
<?php
$pods = pods('name', array('orderby' => 'name asc', 'limit' => -1));
$all_companies = $pods->export_data();
if ( !empty( $all_companies ) ) {
die(json_encode($all_companies);
}else{
die(json_encode(array('error' => 'No cars found.')));
}
?>
I thought about doing something like this:
if ( 0 < $all_companies->total() ) {
while ($all_companies->fetch()) {
$json .= $all_companies->field('title');
}
$json = rtrim($json, ",");
$json .= '}}';
}
echo $json;
But it doesn't work and also the code becomes very long.
I'd make an array of the names of the twenty fields you want then build an array of those fields for each item, by doing a foreach of those field names passed to Pods::field() inside the while loop. Like this:
$pods = pods('name', array('orderby' => 'name asc', 'limit' => -1));
$fields = array( 'field_1', 'field_2' );
if ( $pods->total() > 0 ) {
while ( $pods->fetch() ) {
foreach ( $fields as $field ) {
$json[ $pods->id() ] = $pods->field( $field );
}
}
$json = json_encode( $json );
}
Alternatively, you could hack the /pods/<pod> endpoint of our JSON API to accept a list of fields to return as the body of the request. Wouldn't be hard to do, make sure to submit a pull request if you make it work.

Wordpress MySQL result resource is not valid

I have wp_places custom table and I am getting this when I am printing array:
[0] => stdClass Object
(
[home_location] => 24
)
[1] => stdClass Object
(
[home_location] => 29
)
Now I want to implode value like this way (24,29) but in my code I am getting this error:
<b>Warning</b>: mysql_fetch_array(): supplied argument is not a valid MySQL result resource
My Code
$getGroupType = $_POST['parent_category'];
$result = $wpdb->get_results( "SELECT home_location FROM wp_places WHERE blood_group LIKE '".$getGroupType."%'" );
$bgroup = Array();
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
$bgroup[] = implode(',',$row);
}
echo implode(',',$bgroup);
Any ideas or suggestions? Thanks.
$wpdb->get_results() already do the fetching for you, you don't need to call mysql_fetch_array
Given what you want to do, your code should look like this :
$getGroupType = $_POST['parent_category'];
$result = $wpdb->get_results( "SELECT home_location FROM wp_places WHERE blood_group LIKE '".$getGroupType."%'" );
$bgroup = Array();
foreach ($result as $location) {
$bgroup[] = $location->home_location;
}
echo '('.implode(',',$bgroup).')';
It's an PHP object that contains results, it isn't a MySQL Result.
Looking at the docs, it should be used like
foreach ($result as $row) {
$bgroup[] = $row->home_location;
}
echo implode(',',$bgroup)

Get the fetched values from the database

I need to store the fetched data from the MySQL database. So I used this code.
while (#row = $statement->fetchrow_array)
{
print "#row.\n"; # ------printing data
}
foreach $key(#row)
{
print $key."\n"; # ------empty data
}
In foreach loop the #row data is empty. How to solve this
UPDATE: It actually should be like this:
while (my #row = $statement->fetchrow_array) {
# print "#row.\n";
foreach my $key(#row) {
$query= "ALTER TABLE tablename DROP FOREIGN KEY $key;";
$statement = $connection->prepare($query);
$statement->execute()
or die "SQL Error: $DBI::errstr\n";
}
}
Well, it should be like this:
while (my #row = $statement->fetchrow_array) {
foreach my $key (#row) {
print $key."\n";
}
}
Otherwise all the result set will be consumed in the first loop.
As a sidenote, fetchrow_array returns a rowset as array of field values, not keys. To get the keys as well, you should use fetchrow_hashref:
while (my $row = $statement->fetchrow_hashref) {
while (my ($key, $value) = each %$row) {
print "$key => $value\n";
}
}
UPDATE: from your comments it looks like you actually need an array of column names. There's one way to do it:
my $row = $statement->fetchrow_hashref;
$statement->finish;
foreach my $key (keys %$row) {
# ... dropping $key from db as in your original example
}
But in fact, there are more convenient ways of doing what you want to do. First, there's a single method for preparing and extracting a single row: selectrow_hashref. Second, if you want to get just foreign keys information, why don't use the specific DBI method for that - foreign_key_info? For example:
my $sth = $dbh->foreign_key_info( undef, undef, undef,
undef, undef, $table_name);
my $rows = $sth->fetchall_hashref;
while (my ($k, $v) = each %$rows) {
# process $k/$v
}