In a Catalyst application, I need to generate JSON from DBIx::Class:Core objects.
Such a class definition looks like this:
use utf8;
package My::Schema::Book;
use strict;
use warnings;
use Moose;
use MooseX::NonMoose;
use MooseX::MarkAsMethods autoclean => 1;
extends 'DBIx::Class::Core';
__PACKAGE__->load_components("InflateColumn::DateTime");
__PACKAGE__->table("books");
__PACKAGE__->add_columns(
"id",
{
data_type => "uuid",
default_value => \"uuid_generate_v4()",
is_nullable => 0,
size => 16,
},
"title"
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->meta->make_immutable;
sub TO_JSON {
my $self = shift;
{book => {
id => $self->id,
title => $self->title,
}}
}
1;
After queriyng the books from database I do the encoding of the blessed objects:
$c->stash(books_rs => $c->model('My::Schema::Book'));
$c->stash(books => [$c->stash->{books_rs}->search(
{},
{order_by => 'title ASC'})]
);
$c->stash(json => $json->convert_blessed->encode($c->stash->{books}));
$c->forward('View::JSON');
The JSON output of the query is this:
{"json":"[{\"book\":{\"id\":\"ae355346-8e19-46ee-88ee-773ac30938a9\",\"title\":\"TITLE1\"}},{\"book\":{\"id\":\"9a20f526-d4cd-4e7d-a726-55e78bc3c0ac\",\"title\":\"TITLE2\"}},{\"book\":{\"title\":\"TITLE3\",\"id\":\"1ddb2d27-3ec6-46c1-a1a7-0b151fe44597\"}}]"}
The value of the json key and each particular book key got double quotes what can not be parsed by jQuery. It complains about format exception.
$json->convert_blessed->encode($c->stash->{books}) returns a string. It looks like View::JSON also encodes json.
Try to pass your data as is: $c->stash(json => $c->stash->{books});. You may also need to configure expose_stash and json_encoder_args to handle the right keys from your stash and correctly convert your objects.
See
https://metacpan.org/pod/Catalyst::View::JSON#CONFIG-VARIABLES
Related
I need help pulling data from the field with unknown format of data structure. I am using a wordpress quiz plugin and i want to pull data from its backend table.
Data stored in answer_data is:
a:4:{
i:0;O:27:"WpProQuiz_Model_AnswerTypes":7:{s:10:"*_answer";s:17:"Kieran Trippier ";s:8:"*_html";b:0;s:10:"*_points";i:1;s:11:"*_correct";b:0;s:14:"*_sortString";s:0:"";s:18:"*_sortStringHtml";b:0;s:10:"*_mapper";N;}
i:1;O:27:"WpProQuiz_Model_AnswerTypes":7:{s:10:"*_answer";s:11:"Hugo Lloris";s:8:"*_html";b:0;s:10:"*_points";i:1;s:11:"*_correct";b:0;s:14:"*_sortString";s:0:"";s:18:"*_sortStringHtml";b:0;s:10:"*_mapper";N;}
i:2;O:27:"WpProQuiz_Model_AnswerTypes":7:{s:10:"*_answer";s:14:"Moussa Dembele";s:8:"*_html";b:0;s:10:"*_points";i:1;s:11:"*_correct";b:0;s:14:"*_sortString";s:0:"";s:18:"*_sortStringHtml";b:0;s:10:"*_mapper";N;}
i:3;O:27:"WpProQuiz_Model_AnswerTypes":7:{s:10:"*_answer";s:14:"Jan Vertonghen";s:8:"*_html";b:0;s:10:"*_points";i:1;s:11:"*_correct";b:1;s:14:"*_sortString";s:0:"";s:18:"*_sortStringHtml";b:0;s:10:"*_mapper";N;}
}
while looking at the structure of the table, data type of answer_data is longtext but has utf8_general_ci alongside it. I dont know what this means.
From this data i want to pull, quiz answers i.e.Kieran Trippier,Hugo Lloris,Moussa Dembele and Jan Vertonghen.
Any help or hint will be very much appreciated.
EDIT 1:
Array (
[0] => WpProQuiz_Model_AnswerTypes Object ( [_answer:protected] => Kieran Trippier [_html:protected] => [_points:protected] => 1 [_correct:protected] => [_sortString:protected] => [_sortStringHtml:protected] => [_mapper:protected] => )
[1] => WpProQuiz_Model_AnswerTypes Object ( [_answer:protected] => Hugo Lloris [_html:protected] => [_points:protected] => 1 [_correct:protected] => [_sortString:protected] => [_sortStringHtml:protected] => [_mapper:protected] => )
[2] => WpProQuiz_Model_AnswerTypes Object ( [_answer:protected] => Moussa Dembele [_html:protected] => [_points:protected] => 1 [_correct:protected] => [_sortString:protected] => [_sortStringHtml:protected] => [_mapper:protected] => )
[3] => WpProQuiz_Model_AnswerTypes Object ( [_answer:protected] => Jan Vertonghen [_html:protected] => [_points:protected] => 1 [_correct:protected] => 1 [_sortString:protected] => [_sortStringHtml:protected] => [_mapper:protected] => ) )
how to get values from this array?
That is some form of serialization. It seems to be an array a of 4 elements, each of which is further structure. The plugin understands; you probably don't need to understand it.
utf8_general_ci is the "Collation" to be used for comparing the LONGTEXT strings. It implies CHARACTER SET utf8, which is the 3-byte subset of UTF-8 (aka, MySQL's utf8mb4). This allows you to include characters from most languages around the world.
One would hope that the plugin provides a way to dissect this structure, rather than leaving you guessing. Furthermore, the plugin could change the structure without notice.
Here's the hint: See PHP's serialize() and unserialize().
Using the unserialized result:
$foo seems to be an array of 'objects' of class WpProQuiz_Model_AnswerTypes. One of the 'properties' of that object seems to be $_answer. So, see if this gives you the list of answers:
foreach($foo as $obj) {
echo $obj->_answer, "\n";
}
Or, to grab the answers into an array $answers:
$answers = array();
foreach($foo as $obj) {
$answers[] = $obj->_answer;
}
I am trying to obtain JSON from a DBIx::Class::ResultSet and I get the exception
encountered object 'Sql2Json::Model::DB::Book=HASH(0x6014f88)',
but neither allow_blessed, convert_blessed nor allow_tags settings are
enabled (or TO_JSON/FREEZE method missing)
The controller class is Books.pm:
package Sql2Json::Controller::Books;
use Moose;
use namespace::autoclean;
use JSON::XS;
BEGIN { extends 'Catalyst::Controller'; }
my $json = JSON::XS->new;
sub list : Local {
my($self, $c) = #_;
$c->stash(books_rs => $c->model('DB::Book'));
$c->stash(books => [$c->stash->{books_rs}->search({}, {order_by => 'name ASC'})]);
$c->stash(json_data => $json->convert_blessed->encode($c->stash->{books}));
$c->forward('View::JSON');
}
__PACKAGE__->meta->make_immutable;
1;
According to this article is enough the encoding of blessed objects:
$json->convert_blessed->encode($c->stash->{books})
Do I missing somethig here?
Almost all the time, the best way to do this is using the get_inflated_column method of the rows returned from a query.
$books = $c->model('DB::Book');
$c->stash(json_data => [map {$_->get_inflated_columns} $books->all]);
$c->forward('View::JSON');
I'm trying to use the model create option and this is my array:
$result = array(
'match_id' => $input['id'],
'score' => $input['score'],
'result' => $input['result'],
'headline' => NULL,
'article' => $input['report'],
'tries' => $input['try'],
'try_amount' => $input['tryquant'],
'conversions' => $input['conv'],
'conv_amount' => $input['convquant'],
'penalties' => $input['pens'],
'pen_amount' => $input['penquant'],
'dropgoals' => $input['dgs'],
'dg_amount' => $input['dgquant']
);
Result::create($result);
The contents of some of these are arrays themselves. eg:
$input['penquant'] = [
"4"
]
When I run my code, it saves the data to the DB simply as Array and throws up the following error:
ErrorException in helpers.php line 703: preg_replace(): Parameter mismatch, pattern is a string while replacement is an array
Can someone tell me what I'm doing wrong and how to fix it?
Shouldn't have rushed, forgot to use json_encode.
The best is to use mutators and accessors in your model.
example of mutator
// convert pen_amount from snake case to studly case
// The set...attribute (the ... is you field name which is in studly case) helps transform the data before you save it
// into the database
public function setPenAmountAttribute($value)
{
$this->attributes['pen_amount'] = serialize($value);
}
example of an accessor is
// convert pen_amount from snake case to studly case
// the get...Attribute (the ... is you field name which is in studly case) helps you convert the data back to it original state
public function getPenAmountAttribute($value)
{
return unserialize($value);
}
You can use accessors and mutators for all your fields that you want to save as array. This is elegant. No need to manually use json_encode and decode in your controllers.
i have the following sql statement inside a function..
my $sth = $dbh->prepare(qq[SELECT device_uuid,device_name FROM ].DB_SCHEMA().qq[.user_device WHERE user_id = ?]);
$sth->execute($user_id) || die $dbh->errstr;
the results are being fetched using the following statement
while(my $data = $sth->fetchrow_arrayref()) {
}
my question is how can i create and return a json structure containing objects for every row being fetched?something like this
{
object1:{
"device_uuid1":"id1",
"device_name1":"name1"
},
object2:{
"device_uuid2":"id2",
"device_name2":"name2"
},
object3:{
"device_uuid3":"id3",
"device_name3":"name3"
}
}
the total number of json objects will be equal to the number of rows returned by the sql statement.
i have managed to build the structure like this
$VAR1 = [{"device_name":"device1","device_id":"device_id1"},{"device_name":"device2","device_id":"device_id2"}]
how can i iterate through the array refs and get "device_name" and "device_id" values?
For your needs, this library should work well. What you need to do is have a scalar variable defined as below and push the element for each iteration in while loop
my $json = JSON->new->utf8->space_after->encode({})
while(my $data = $sth->fetchrow_arrayref()) {
#Push new element here in $json using incr_parse method
#or using $json_text = $json->encode($perl_scalar)
}
Hope this helps you.
finally what i did was to create an array ref and push the fetched rows which are being returned as hash refs
my #device = ();
while(my $data = $sth->fetchrow_hashref()) {
push(#device, $data);
}
last i convert the #device array ref to json and return the outcome
return encode_json(\#device);
The statement handle method fetchall_arrayref() can return an array reference where each element in the referenced array is a hash reference containing details of one row in the resultset. This seems to me to be exactly the data structure that you want. So you can just call that method and pass the returned data structure to a JSON encoding function.
# Passing a hash ref to fetchall_arrayref() tells it to
# return each row as a hash reference.
my $json = encode_json($sth->fetchall_arrayref({});
Your sample JSON is incorrect - JSON is actually quite nicely represented by perl data structures - [] denotes array, {} denotes key-value (very similar to hash).
I would rather strongly suggest though, that what you've asked for is probably not what you want - you've seemingly gone for globally unique keys, which ... isn't good style when they're nested.
Why? well, so you can do things like this:
print $my_data{$_}->{'name'} for keys %my_data;
Far better to go for something like:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
use JSON;
my %my_data = (
object1 => {
uuid => "id1",
name => "name1"
},
object2 => {
uuid => "id2",
name => "name2"
},
object3 => {
uuid => "id3",
name => "name3"
},
);
print Dumper \%my_data;
print to_json ( \%my_data, { 'pretty' => 1 } )."\n";
Now, that does assume your 'object1' is a unique key - if it isn't, you can instead do something like this - an array of anonymous hashes (for bonus points, it preserves ordering)
my #my_data = (
{ object1 => {
uuid => "id1",
name => "name1"
}
},
{ object2 => {
uuid => "id2",
name => "name2"
}
},
{ object3 => {
uuid => "id3",
name => "name3"
}
},
);
Now, how to take your example and extend it? Easy peasy really - assemble what you want to add to your structure in your loop, and insert it into the structure:
while(my $data = $sth->fetchrow_arrayref()) {
my $objectname = $data -> [0]; #assuming it's this element!
my $uuid = $data -> [1];
my $name = $data -> [2];
my $new_hash = { uuid => $uuid, name => $name };
$mydata{$objectname} = $new_hash;
}
I need to get the data into a JSON object but because I'm using the %data hash and it has the same address I'm getting the same data repeatedly in my JSON object.
This is the code that produces the JSON.
while (my ($orderID, $possessorName, $itemDescription, $...) = $sth->fetchrow_array)
{
%data = (orderID => $orderID, possessorName => $possessorName, itemDescription => $itemDescription,...);
$query_results{"job$index"} = {"data" => \%data};
$index++;
}
return $json_obj->pretty->encode(\%query_results, {ascii => 1, pretty => 1});
The problem is that the last item in my data set is masking all the previous items so I end up with one large JSON of the same exact data. I could use an array of hashes I suppose but this seems really messy and sloppy. How do I write the cleanest code to get my data? If an array of hashes is the best way to go please let me know and I'll do it. I all ready know how or can figure it out on my own.
What happens when you try:
my $index = 0;
my %query_results;
while (my ($orderID, $possessorName, $itemDescription, $...) = $sth->fetchrow_array) {
my %data = (orderID => $orderID, possessorName => $possessorName, itemDescription => $itemDescription,...);
$query_results{"job$index"}{'data'} = \%data;
$index++;
}
Previously, you used a %data hash declared in an outside scope; or worse, you didn't use strict; use warnings so %data was in fact an implicit global. Now, we declare the %data inside the loop which makes all the hashes distinct.
You could also copy the hash into a new hashref by {%data}.
That said, you don't even need that variable:
$query_results{"job$index"}{data} = {
# anonymous hashref here
orderID => $orderId,
possessorName => $possessorName,
itemDescription => ...
};