Data from JQUERY/AJAX to Morris.JS - javascript

I'm trying to export data from AJAX/JQUERY to Morris.JS.
Variable datachart return with data. but morris.js graph returns no Line/bar
$("#diseaseselection").change(function(){
$("#chart").empty();
var diseaseselection = $("#diseaseselection").val();
$.ajax({
url: "chart.php",
method: "POST",
data: {
diseaseselection: diseaseselection
},
success: function(data) {
Morris.Line({
element : 'chart',
data:[data],
xkey:'age',
ykeys:[ 'totalM', 'totalF'],
labels:['Total MALE', 'Total FEMALE'],
hideHover:'auto',
pointStrokeColors: ['white'],
lineWidth:'6px',
parseTime: false,
lineColors: ['Skyblue', 'Pink'],
});
}
});
});
Here is my sample PHP code
Please help me how to figure it out i badly need it thanks alot man. already trying my best
$diseaseselection = $_REQUEST['diseaseselection'];
if(isset($diseaseselection)){
$result = mysqli_query($con, "SELECT disease,age,SUM(CASE WHEN gender = 'm' THEN 1 ELSE 0 END) AS totalM, SUM(CASE WHEN gender = 'f' THEN 1 ELSE 0 END) AS totalF FROM mdr where disease = '$diseaseselection' GROUP BY disease , age");
$chart_data = '';
while($row = mysqli_fetch_array($result)) {
$chart_data .= "{ age:'".$row["age"]."', totalM:".$row["totalM"].", totalF:".$row["totalF"]."}, ";
}
$chart_data = substr($chart_data, 0, -2);
echo $chart_data; }
Here is my sample Output
This is based on my console log console.log(data);
Please help me how to figure it out i badly need it thanks alot man. already trying my best
{ age:'0-1', totalM:2, totalF:1},
{ age:'1-4', totalM:1, totalF:0},
{ age:'10-14', totalM:0, totalF:1},
{ age:'15-19', totalM:0, totalF:1},
{ age:'5-9', totalM:0, totalF:3},
{ age:'55-59', totalM:6, totalF:0}

There are a number of little issues here, which are kind of all tied up in the same key problem - what your PHP is producing is not valid JSON data.
If you copy and paste your sample data into a validator such as JSONLint you'll that it fails in a couple of ways:
1) You've got a list of objects, but in order to be a valid list (or array, as it's usually known) the items must be wrapped in square brackets ([ and ]) at the beginning and end.
2) The property names (e.g. age, totalM, and totalF) must have double quote marks (") around them.
3) The string values (e.g. 0-1, 1-4 etc) must have double quote marks around them, not single quote marks.
A valid version of your sample JSON would look like this:
[
{ "age": "0-1", "totalM": 2, "totalF": 1 },
{ "age": "1-4", "totalM": 1, "totalF": 0 },
{ "age": "10-14", "totalM": 0, "totalF": 1 },
{ "age": "15-19", "totalM": 0, "totalF": 1 },
{ "age": "5-9", "totalM": 0, "totalF": 3 },
{ "age": "55-59", "totalM": 6, "totalF": 0 }
]
You might find this tutorial useful as a quick way to learn the syntax.
However, useful as it is to know the syntax, you don't actually have to create it manually via your PHP, as you are doing now. In fact that's quite a bad idea to do that, because it leaves you vulnerable to silly mistakes (like not adding the square brackets), and at risk of accidental syntax errors in the JSON (e.g. imagine one of your string values itself contained a double-quote mark: if you didn't use a suitable escape character in front of it, then in the JSON it would look like the end of the property, and what followed would then be invalid).
The result of the problems above is that your PHP returns a string of invalid data back to the browser, and that cannot be used to populate the chart.
It's far better to simply construct a normal array in PHP, and then use the built-in json_encode() function to take care of turning that object into valid JSON. This is commonly accepted as best practice, and if you follow any introductory PHP/JSON tutorial it will show this function to you.
To add to the problems creating the JSON server-side, there's a client-side issue too: even if you did return valid JSON, at that point it's still a string - in order for it to be used in your chart you'd have to parse it into a JavaScript variable. If you specify dataType: "json" in your $.ajax options, jQuery will do the parsing for you automatically. Otherwise, you would make a call to JSON.parse() to do it.
Hopefully you see the overall pattern now - you take a PHP variable and turn it into JSON, which is a text representation of the data. This allows you to send it across the internet. Then when it arrives at the destination, you turn it back into a (JavaScript) variable again to be used in the code.
Here's some example PHP which will generate valid JSON in the recommended way. I've added comments at important lines:
$diseaseselection = $_REQUEST['diseaseselection'];
if(isset($diseaseselection)){
$result = mysqli_query($con, "SELECT disease,age,SUM(CASE WHEN gender = 'm' THEN 1 ELSE 0 END) AS totalM, SUM(CASE WHEN gender = 'f' THEN 1 ELSE 0 END) AS totalF FROM mdr where disease = '$diseaseselection' GROUP BY disease , age");
$chart_data = array(); //declare an array, not a string. This will become the outer array of the JSON.
while($row = mysqli_fetch_array($result)) {
//add a new item to the array
//each new item is an associative array with key-value pairs - this will become an object in the JSON
$chart_data [] = array(
"age" => $row["age"],
"totalM" => $row["totalM"],
"totalF" => $row["totalF"]
);
}
$json = json_encode($chart_data); //encode the array into a valid JSON object
echo $json; //output the JSON
}
And here's the relevant part of the JavaScript code to receive it
$.ajax({
url: "chart.php",
method: "POST",
data: {
diseaseselection: diseaseselection
},
dataType: "json", //parse the response data as JSON automatically
success: function(data) {
Morris.Line({
element: 'chart',
data: data, //supply the response data (which is now a JS variable) directly, no extra brackets
xkey: 'age',
ykeys: ['totalM', 'totalF'],
labels: ['Total MALE', 'Total FEMALE'],
hideHover: 'auto',
pointStrokeColors: ['white'],
lineWidth: '6px',
parseTime: false,
lineColors: ['Skyblue', 'Pink'],
});
}
});
Here's a working demo of just the AJAX and chart part (using a dummy server to provide the JSON): https://jsfiddle.net/7o9ptajr/1/

Related

How can I reformat this simple JSON so it doesn't catch "Circular structure to JSON" exception?

Introduction
I'm learning JavaScript on my own and JSON its something along the path. I'm working on a JavaScript WebScraper and I want, for now, load my results in JSON format.
I know I can use data base, server-client stuff, etc to work with data. But I want to take this approach as learning JSON and how to parse/create/format it's my main goal for today.
Explaining variables
As you may have guessed the data stored in the fore mentioned variables comes from an html file. So an example of the content in:
users[] -> "Egypt"
GDP[] -> "<td> $2,971</td>"
Regions[] -> "<td> Egypt </td>"
Align[] -> "<td> Eastern Bloc </td>"
Code
let countries = [];
for(let i = 0; i < users.length; i++)
{
countries.push( {
'country' : [{
'name' : users[i],
'GDP' : GDP[i],
'Region' : regions[i],
'Align' : align[i]
}]})
};
let obj_data = JSON.stringify(countries, null, 2);
fs.writeFileSync('countryballs.json', obj_data);
Code explanation
I have previously loaded into arrays (users, GDP, regionsm align) those store the data (String format) I had extracted from a website.
My idea was to then "dump" it into an object with which the stringify() function format would format it into JSON.
I have tested it without the loop (static data just for testing) and it works.
Type of error
let obj_data = JSON.stringify(countries, null, 2);
^
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Node'
| property 'children' -> object with constructor 'Array'
| index 0 -> object with constructor 'Node'
--- property 'parent' closes the circle
What I want from this question
I want to know what makes this JSON format "Circular" and how to make this code work for my goals.
Notes
I am working with Node.js and Visual Studio Code
EDIT
This is further explanation for those who were interested and thought it was not a good question.
Test code that works
let countries;
console.log(users.length)
for(let i = 0; i < users.length; i++)
{
countries = {
country : [
{
"name" : 'CountryTest'
}
]
}
};
let obj_data = JSON.stringify(countries, null, 2);
fs.writeFileSync('countryballs.json', obj_data);
});
Notice in comparison to the previous code, right now I am inputing "manually" the name of the country object.
This way absolutely works as you can see below:
Now, if I change 'CountryTest' to into a users[i] where I store country names (Forget about why countries are tagged users, it is out of the scope of this question)
It shows me the previous circular error.
A "Partial Solution" for this was to add +"" which, as I said, partially solved the problem as now there is not "Circular Error"
Example:
for(let i = 0; i < users.length; i++)
{
countries = {
country : [
{
"name" : users[i]+''
}
]
}
};
Resulting in:
Another bug, which I do not know why is that only shows 1 country when there are 32 in the array users[]
This makes me think that the answers provided are not correct so far.
Desired JSON format
{
"countries": {
"country": [
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
},
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
},
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
}
]}
}
Circular structure error occurs when you have a property of the object which is the object itself directly (a -> a) or indirectly (a -> b -> a).
To avoid the error message, tell JSON.stringify what to do when it encounters a circular reference. For example, if you have a person pointing to another person ("parent"), which may (or may not) point to the original person, do the following:
JSON.stringify( that.person, function( key, value) {
if( key == 'parent') { return value.id;}
else {return value;}
})
The second parameter to stringify is a filter function. Here it simply converts the referred object to its ID, but you are free to do whatever you like to break the circular reference.
You can test the above code with the following:
function Person( params) {
this.id = params['id'];
this.name = params['name'];
this.father = null;
this.fingers = [];
// etc.
}
var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him;
JSON.stringify(me); // so far so good
him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
if(key == 'father') {
return value.id;
} else {
return value;
};
})
The answer is from StackOverflow question,
Stringify (convert to JSON) a JavaScript object with circular reference
From your output, it looks as though users is a list of DOM nodes. Rather than referring to these directly (where there are all sort of possible cyclical structures), if you just want their text, instead of using users directly, try something like
country : [
{
"name" : users[i].textContent // maybe also followed by `.trim()
}
]
Or you could do this up front to your whole list:
const usersText = [...users].map(node => node.textContent)
and then use usersText in place of users as you build your object.
If GDP, regions and align are also references to your HTML, then you might have to do the same with them.
EUREKA!
As some of you have mentioned above, let me tell you it is not a problem of circularity, at first..., in the JSON design. It is an error of the data itself.
When I scraped the data it came in html format i.e <td>whatever</td>, I did not care about that as I could simply take it away later. I was way too focused in having the JSON well formatted and learning.
As #VLAZ and #Scott Sauyezt mentioned above, it could be that some of the data, if it is not well formatted into string, it might be referring to itself somehow as so I started to work on that.
Lets have a look at this assumption...
To extract the data I used the cheerio.js which gives you a kind of jquery thing to parse html.
To extract the name of the country I used:
nullTest = ($('table').eq(2).find('tr').eq(i).find('td').find('a').last());
//"Partial solution" for the OutOfIndex nulls
if (nullTest != null)
{
users.push(nullTest);
}
(nullTest helps me avoid nulls, I will implement some RegEx when everything works to polish the code a bit)
This "query" would output me something like:
whatEverIsInHereIfThereIsAny
or else.
to get rid off this html thing just add .html() at the end of the "jquery" such as:
($('table').eq(2).find('tr').eq(i).find('td').find('a').last().html());
That way you are now working with String and avoiding any error and thus solves this question.

JQuery unable to parse JSON string created by json_encode

I am currently in a bind, JQuery is unable to parse the following json strings
{ "query":"Unit",
"suggestions":
[ {"value":"Mr Ruto Kimutai ","data":88},{"value":"Mr Kimani Karanja","data":79} ] }
{"query":"Unit",
"suggestions":
[{"value":"Mr Ruto Kimutai ","data":88},{"value":"Mr Kimani Karanja","data":79}]}
The above strings when parse through JSON.parse create the following arror:
SyntaxError: JSON.parse: unexpected non-whitespace character after
JSON data at line 1 column 112 of the JSON data
The PHP code which creates the string above is this:
public function getCustomerSuggestions($name){
$customers = $this->model->where('name','LIKE','%'.$name.'%')->show();
if(count($customers)>=1){
foreach($customers as $customer){
$list[] = ['value' => ucfirst($customer->name),'data' => $customer->id];
}
}
else{
$list[] = ['value' => 'No Customers Found', 'data'=> NULL];
}
$full_list['query'] = 'Unit';
$full_list['suggestions'] = $list;
return json_encode($full_list);
}
As you can see I am using the function json_encode to create the JSOn string so there should be no issue but it still doesnt work.
Edit
The json is sent using an autocomplete tool called DevBridge Autocomplete which takes the JSON strings and creates a suggestion list. The code I am using is
$('input[name=\"customer\"]').devbridgeAutocomplete({
serviceUrl: '".SITE_PATH."/ajax/admin/quotes/getcustomer',
minChars: 1,
onSearchStart: function (query){
var searchinput = $(this).val();
$('.autocomplete-suggestions').html('Searching: '+searchinput);
},
onSelect: function(suggestion){
var selection = $(this).val(suggestion.value);
$('input[name=\"customerid\"]').val(suggestion.data);
$.get('".SITE_PATH."/ajax/admin/quotes/getcustomerdetails',{id: suggestion.data},
function(response){
var obj = $.parseJSON(response);
$.each(obj, function(key, value){
$('#'+key).val(value);
});
});
}
});
It seems you have two JSON objects after each other. That's simply invalid. There can only be a single value at the root of a JSON "document". If you want to send down multiple objects, you need to put them in an array.
It seems getCustomerSuggestions is called multiple times and the return value of each call is returned to the client. Instead, the method should return an array, the caller should collect the return values in an array and JSON encode that array.
Well, your JSON string is NOT valid.
It should be,
[
{ "query":"Unit",
"suggestions":
[ {"value":"Mr Ruto Kimutai ","data":88},{"value":"Mr Kimani Karanja","data":79} ] }
,
{"query":"Unit",
"suggestions":
[{"value":"Mr Ruto Kimutai ","data":88},{"value":"Mr Kimani Karanja","data":79}]}
]
But as Felix Kling said, check your PHP code.

Perform "javascript/jQuery-like" functions using PHP

I'm trying to move some processing from client to server side.
I am doing this via AJAX.
In this case t is a URL like this: https://itunes.apple.com/us/podcast/real-crime-profile/id1081244497?mt=2&uo=2.
First problem, I need to send a bunch of these URLs through this little function, to just pull out "1081244497" using my example. The following accomplishes this in javascript, but not sure how to make it loop in PHP.
var e = t.match(/id(\d+)/);
if (e) {
podcastid= e[1];
} else {
podcastid = t.match(/\d+/);
}
The next part is trickier. I can pass one of these podcastid at a time into AJAX and get back what I need, like so:
$.ajax({
url: 'https://itunes.apple.com/lookup',
data: {
id: podcastid,
entity: 'podcast'
},
type: 'GET',
dataType: 'jsonp',
timeout: 5000,
success: function(data) {
console.log(data.results);
},
});
What I don't know how to do is accomplish this same thing in PHP, but also using the list of podcastids without passing one at a time (but that might be the only way).
Thoughts on how to get started here?
MAJOR EDIT
Okay...let me clarify what I need now given some of the comments.
I have this in PHP:
$sxml = simplexml_load_file($url);
$jObj = json_decode($json);
$new = new stdClass(); // create a new object
foreach( $sxml->entry as $entry ) {
$t = new stdClass();
$t->id = $entry->id;
$new->entries[] = $t; // create an array of objects
}
$newJsonString = json_encode($new);
var_dump($new);
This gives me:
object(stdClass)#27 (1) {
["entries"]=>
array(2) {
[0]=>
object(stdClass)#31 (1) {
["id"]=>
object(SimpleXMLElement)#32 (1) {
[0]=>
string(64) "https://itunes.apple.com/us/podcast/serial/id917918570?mt=2&uo=2"
}
}
[1]=>
object(stdClass)#30 (1) {
["id"]=>
object(SimpleXMLElement)#34 (1) {
[0]=>
string(77) "https://itunes.apple.com/us/podcast/real-crime-profile/id1081244497?mt=2&uo=2"
}
}
}
}
What I need now is to pull out each of the strings (the URLs) and then run them through a function like the following to just end up with this: "917918570,1081244497", which is just a piece of the URL, joined by a commas.
I have this function to get the id number for one at a time, but struggling with how the foreach would work (plus I know there has to be a better way to do this function):
$t="https://itunes.apple.com/us/podcast/real-crime-profile/id1081244497?mt=2&uo=2";
$some =(parse_url($t));
$newsome = ($some['path']);
$bomb = explode("/", $newsome);
$newb = ($bomb[4]);
$mrbill = (str_replace("id","",$newb,$i));
print_r($mrbill);
//outputs 1081244497
find match preg_match() and http_build_query() to turn array into query string. And file_get_contents() for the request of the data. and json_decode() to parse the json responce into php array.
in the end it should look like this.
$json_array = json_decode(file_get_contents('https://itunes.apple.com/lookup?'.http_build_query(['id'=>25,'entity'=>'podcast'])));
if(preg_match("/id(\d+)/", $string,$matches)){
$matches[0];
}
You may have to mess with this a little. This should get you on the right track though. If you have problems you can always use print_r() or var_dump() to debug.
As far as the Apple API use , to seperate ids
https://itunes.apple.com/lookup?id=909253,284910350
you will get multiple results that come back into an array and you can use a foreach() loop to parse them out.
EDIT
Here is a full example that gets the artist name from a list of urls
$urls = [
'https://itunes.apple.com/us/podcast/real-crime-profile/id1081244497?mt=2&uo=2.',
'https://itunes.apple.com/us/podcast/dan-carlins-hardcore-history/id173001861?mt=2'
];
$podcast_ids = [];
$info = [];
foreach ($urls as $string) {
if (preg_match('/id(\d+)/', $string, $match)) {
$podcast_ids[] = $match[1];
}
}
$json_array = json_decode(file_get_contents('https://itunes.apple.com/lookup?' . http_build_query(['id' => implode(',', $podcast_ids)])));
foreach ($json_array->results as $item) {
$info[] = $item->artistName;
}
print '<pre>';
print_r($info);
print '</pre>';
EDIT 2
To put your object into an array just run it through this
foreach ($sxml->entries as $entry) {
$urls[] = $entry->id[0];
}
When you access and object you use -> when you access an array you use []. Json and xml will parse out in to a combination of both objects and arrays. So you just need to follow the object's path and put the right keys in the right places to unlock that gate.

How to properly loop through JSON response

Im trying to populate a combobox with data from database.
When I access mystream.php?theLocation=NewYork
I get this JSON response
RESULT
{"result":
[{"theID":"36"},{"theStream":"0817-05131"},{"theLabel":"hgjbn"},{"theLocation":"NewYork"},
{"theID":"37"},{"theStream":"0817-05131"},{"theLabel":"hgjbn"},{"theLocation":"NewYork"},
{"theID":"40"},{"theStream":"0817-31334"},{"theLabel":"dsfg ghjg"},{"theLocation":"NewYork"}]}
Applying the answer from this post
loop through JSON result with jQuery
I came up with this JSON
$.getJSON(
'mystream.php',
'theLocation=NewYork',
function(result){
$('#cmbNewYork').empty();
$.each(result, function(i, item){
$('#cmbNewYork').append('<option value=' +item.theStream+ '>'+item.theLabel+'</option>');
alert(item.theStream);
});
}
);
My resulting combo box only contains undefined.
How to properly loop thru JSON response?
Thanks
EDIT (ADDED)
mystream.php
$sql = "SELECT * FROM Streams WHERE theLocation='$loc'";
$res = mysqli_query($conn,$sql);
$result = array();
while($row = mysqli_fetch_array($res)){
array_push($result,
array('theID'=>$row['theID']),
array('theStream'=>$row['theStream']),
array('theLabel'=>$row['theLabel']),
array('theLocation'=>$row['theLocation'])
);
}
echo json_encode(array('result'=>$result));
Two issues:
The primary issue that your JSON format is very strange: It's an array of objects each of which has one name/value pair:
{"result": [
{"theID":"36"},
{"theStream":"0817-05131"},
{"theLabel":"hgjbn"},
{"theLocation":"NewYork"},
{"theID":"37"},
{"theStream":"0817-05131"},
{"theLabel":"hgjbn"},
{"theLocation":"NewYork"},
{"theID":"40"},
{"theStream":"0817-31334"},
{"theLabel":"dsfg ghjg"},
{"theLocation":"NewYork"}
]}
That's 12 separate objects.
You should have objects with all of those properties together:
{
"result": [
{
"theID": "36",
"theStream": "0817-05131",
"theLabel": "hgjbn",
"theLocation": "NewYork"
},
{
"theID": "37",
"theStream": "0817-05131",
"theLabel": "hgjbn",
"theLocation": "NewYork"
},
{
"theID": "40",
"theStream": "0817-31334",
"theLabel": "dsfg ghjg",
"theLocation": "NewYork"
}
]
}
That's three objects, each with four properties.
Re your edit to the question, you can do that like this:
while($row = mysqli_fetch_array($res)){
array_push($result,
array(
'theID'=>$row['theID'],
'theStream'=>$row['theStream'],
'theLabel'=>$row['theLabel'],
'theLocation'=>$row['theLocation']
)
);
}
Note how that's creating one array per loop, rather than four.
The second issue is that you probably need result.result, rather than just result, on this line:
$.each(result.result, function(i, item){
// ----------^^^^^^^
...since result is your overall anonymous result, which has a single property, result, which has your array.
If you fix those, your loop should start working.
You don't have to do the result.result thing if you don't want to. Instead, you could have your JSON define an array instead of an object with a single property referring to the array:
[
{
"theID": "36",
"theStream": "0817-05131",
"theLabel": "hgjbn",
"theLocation": "NewYork"
},
(and so on)
]
You haven't shown the PHP code creating $result, so I can't show you how to do that, but A) You don't need to, the result.result thing is fine, and B) If you want to, I'm sure you can figure it out.

Properly returning a JSON object with an embedded JS function

Updated Post
I have a solution for that problem which works with me so far (only tested on Firefox).
My main goal was to dynamically update a Flot graph with Ajax. I also need to dynamically update the axis styles and you can do this by the tickFormatter option. To avoid the problems I previously had, I simply insert the desired return string in the PHP array and JSON encode that array. After returned to the Ajax function I create a new function using the javascript Function() constructor (Link). I hope this helps someone.
Ajax
$.ajax({
url: 'someControllerWhichCreatesJSONdata',
data: 'some Value',
type: 'post',
cache: false,
datatype: 'json'
})
.success(function(data) {
var datas = data[0]; // fetch data array
var options = data[1]; // fetch options array
var xReturn = options.xaxes[0].tickFormatter; // fetch return string
var yReturn = options.yaxes[0].tickFormatter;
var xFunc = new Function("val", "axis", xReturn); // create function
var yFunc = new Function("val", "axis", yReturn);
options.xaxes[0].tickFormatter = xFunc; // update tickFormatter with function
options.yaxes[0].tickFormatter = yFunc;
$.plot($("#yw0"), datas, options);
})
.error(function(data) {
$("#error").html(data.responseText); // show error in separate div
});
PHP array
$options = array(
'legend' => array(
'position' => 'nw',
'show' => true,
'margin' => 10,
'backgroundOpacity' => 0.5
),
'grid' => array(
'clickable' => true,
'hoverable' => true
),
'pan' => array('interactive' => true),
'zoom' => array('interactive' => true),
'axisLabels' => array('show' => true),
'xaxes' => array(
array(
'axisLabel' => 'someUnit',
'tickFormatter' => 'return "foo bar"' // enter whatever return value you need
)
),
'yaxes' => array(
array(
'axisLabel' => 'someUnit',
'tickFormatter' => 'return "foo bar"'
)
)
);
I skip the data array as it looks similar. Both arrays get combined and JSON encoded.
public function actionSomeControllerWhichCreatesJSONdata() {
$returnArray = array($data, $options);
echo json_encode($returnArray);
return true;
}
The resulting array looks like the one I've posted in the bottom of this post.
Original Post
I'm trying for hours now, but I can't get this to work.
I have an Ajax request which gets a JSON object as return value on success. This works fine as long as I don't use a JS function in my JSON array. So my JSON array looks as follows (after json_encode):
[{
"data": [{
{1,2},
{3,4},
}],
"function": "function(){return \"foo bar\";}"
}]
In order to get rid of the "'s in the function string. I use str_replace and replace the quoted string with an unquoted one. This then looks like so:
[{
"data": [{
{1,2},
{3,4},
}],
"function": function(){return "foo bar";}
}]
The plain json_encode works fine and my Ajax function reads it as a JSON object. After I replace the string it turns out that the return value is no longer a JSON object but a plain text string. I've tried to parse the string again with jquery.parseJSON() but this results in the syntax error:
SyntaxError: JSON.parse: unexpected keyword at line 1 column 396 of the JSON data
Ajax function:
$.ajax({
url: 'someControllerWhichCreatesJSONdata',
data: 'some Value',
type: 'post',
cache: false,
datatype: 'json' // or text when trying to parse
})
.success(function(data) {
console.log(data);
var dat = jQuery.parseJSON(data); // trying to parse (only when dataType: "text")
var datas = dat[0]; // or data[0] when datType = json
var options = dat[1]; // ---
$.plot($("#yw0"), datas, options); // trying to update a FLOT window
})
.error(function(data) {
console.log("error");
$("#error").html(data.responseText); // show error in separate div
});
So when using this function and setting the return type to "json", it produces an error. If the return type is "text" and I try to parse the string I get the error above. Is there any solution for this problem or am I doing something completely wrong?
Thank's for your help!
UPDATE
Sorry of course my JSON data is not valid! As I said, my JSON object gets created by json_encode which I guess should get the syntax right. The plain array after son_encode looks like:
[[{
"data":[[0.0042612,0.0042612]],
"label":"WISE.W3: Cutri et. al 2012",
"lines":{"show":false},
"points":{"show":true,"radius":3}}],
{
"legend": {
"position":"nw",
"show":true,
"margin":10,
"backgroundOpacity":0.5},
"grid": {
"clickable":true,
"hoverable":true},
"pan":{"interactive":true},
"zoom":{"interactive":true},
"axisLabels":{"show":true},
"axisLabel": {
"unit":"Jy",
"id":null,
"name":null},
"tickFormatter":"$tickFormatter$",
"position":"right"
}]
and after str_replace I get
[[{
"data":[[0.0042612,0.0042612]],
"label":"WISE.W3: Cutri et. al 2012",
"lines":{"show":false},
"points":{"show":true,"radius":3}}],
{
"legend": {
"position":"nw",
"show":true,
"margin":10,
"backgroundOpacity":0.5},
"grid":{"clickable":true,"hoverable":true},
"pan":{"interactive":true},
"zoom":{"interactive":true},
"axisLabels":{"show":true},
"axisLabel":{"unit":"Jy","id":null,"name":null},
"tickFormatter":function(val, axis){return val.toExponential(2)},
"position":"right"
}]
Adeno pointed it out already, your JSON syntax is not valid.
Please try to validate your JSON with a linter, like http://jsonformatter.curiousconcept.com/
This is a bit better:
[{"data":[["1","2"],["3","4"]],"function":"function(){return \"foo bar\";}"}]
And the other thing is: you should not manually deserialize the JSON. All newer versions of jQuery will automatically deserialize JSON based on the response's content-type header.
You already set dataType to json for the ajax request.
The response is JSON and will be de-serialized.
So you can directly use
.success: function(response) {
console.log(response.data);
console.log(response.function);
}
Answer for the questions/issue from the comments:
Now you created invalid JSON again, because "tickFormatter" : function(val, axis){return val.toExponential(2)}, misses the quotes around the function string.
Before you insert the string with str_replace(), you need to take care of escaping and quoting.
You may need a little helper function, which properly escapes a string - to be valid JSON.
you need to use str_replace() correctly: not replacing the outer quotes, just the inner $tickFormatter$.
Wait.. i will provide an example:
// we already have an array, which is JSON encoded
// now you want to insert additional JSON content.
// therefore we use a token replace approach.
// valid JSON - with $token$
$json = '[{"data":[["1","2"],["3","4"]],"tickFormatter":"$tickFormatter$"}]';
// the (future) javascript function is a string.
// it's not properly escaped or quoted to be JSON
// this allows for copy and pasting JS into PHP
$unescaped_functionString = 'function(){return "foo bar";}';
// in order escapre properly, we need a json escaping helper
/**
* #param $value
* #return mixed
*/
function escapeJsonString($value) { # list from www.json.org: (\b backspace, \f formfeed)
$escapers = array("\\", "/", "\"", "\n", "\r", "\t", "\x08", "\x0c");
$replacements = array("\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b");
$result = str_replace($escapers, $replacements, $value);
return $result;
}
// then we escape the function string
$functionString = escapeJsonString($unescaped_functionString);
// now its ready to be inserted into the existing JSON,
// where token $tickFormatter$ is defined
// the token must be in single quotes. you replace just the $token$ and not "$token$".
$json = str_replace('$tickFormatter$', $functionString, $json);
echo $json;
// The result is valid JSON again:
// [{"data":[["1","2"],["3","4"]],"tickFormatter":"function(){return \"foo bar\";}"}]
Next step:
The ajax request is made, the json data is transferred to the client and you want to execute the function you passed in.
So the new question is "How to call/exec a JavaScript function from a string - without using eval?"
In general tunneling is a bad practice, see: Is it valid to define functions in JSON results?
Anyway, there a different ways to do this:
Referencing: http://everythingfrontend.com/posts/studying-javascript-eval.html
eval() - evil, but works.
setTimeout()
setTimeout(" function ", 0);
new Function()
var codeToExecute = "My.Namespace.functionName()";
var tmpFunc = new Function(codeToExecute);
tmpFunc();
document.write
document.write('<script> JS </script>')
data URI
var s = document.createElement('script');
s.src = 'data:text/javascript,' + encodeURIComponent('alert("lorem ipsum")')
document.body.appendChild(s);
JSON cannot contain functions like this.
You can however use Javascript to inject the function into the DOM, so what you want to do is skip the str_replace part and simply create a <script type="text/javascript"></script> element in the DOM and insert the data.function property into there.

Categories

Resources