PHP reading invalid json with json_decode(); - php

I have invalid external json data, without double quotes around names.
Example:
{
data: [
{
idx: 0,
id: "0",
url: "http://247wallst.com/",
a: [
{
t: "Title",
u: "http://247wallst.com/2012/07/30/",
sp: "About"
}
],
doc_id: "9386093612452939480"
},
{
idx: 1,
id: "-1"
}
],
results_per_page: 10,
total_number_of_news: 76,
news_per_month: [20, 0, 8, 1, 1, 2, 0, 2, 1, 0, 0, 1, 1, 0, 5, 1, 1, 1, 0, 2, 5, 16, 7, 1],
result_start_num: 2,
result_end_num: 2,
result_total_articles: 76
}
As you see a lot of names like data,idx,id,url and others are not double quoted, so this makes this json invalid.
How can I make this external json valid? I already tried str_replace, replacing '{' to '{"' and ':' to '":' adding double quotes around unquoted names, but this messes up some already double quoted variables.
How can I make this json valid so I can read this data with PHP json_decode? I'm not very familiar with preg_replace..
Valid json will look like:
{
"data": [
{
"idx": 0,
"id": "0",
"url": "http://247wallst.com/",
"a": [
{
"t": "Title",
"u": "http://247wallst.com/2012/07/30/",
"sp": "About"
}
],
"doc_id": "9386093612452939480"
},
{
"idx": 1,
"id": "-1"
}
],
"results_per_page": 10,
"total_number_of_news": 76,
"news_per_month": [20, 0, 8, 1, 1, 2, 0, 2, 1, 0, 0, 1, 1, 0, 5, 1, 1, 1, 0, 2, 5, 16, 7, 1],
"result_start_num": 2,
"result_end_num": 2,
"result_total_articles": 76
}
Please suggest me some php preg_replace function.
Data source:
http://www.google.com/finance/company_news?q=aapl&output=json&start=1&num=1

With preg_replace you can do:
json_decode(preg_replace('#(?<pre>\{|\[|,)\s*(?<key>(?:\w|_)+)\s*:#im', '$1"$2":', $in));
Since the above example won't work with real data (the battle plans seldom survive first contact with the enemy) heres my second take:
$infile = 'http://www.google.com/finance/company_news?q=aapl&output=json&start=1&num=1';
// first, get rid of the \x26 and other encoded bytes.
$in = preg_replace_callback('/\\\x([0-9A-F]{2})/i',
function($match){
return chr(intval($match[1], 16));
}, file_get_contents($infile));
$out = $in;
// find key candidates
preg_match_all('#(?<=\{|\[|,)\s*(?<key>(?:\w|_)+?)\s*:#im', $in, $m, PREG_OFFSET_CAPTURE);
$replaces_so_far = 0;
// check each candidate if its in a quoted string or not
foreach ($m['key'] as $match) {
$position = $match[1] + ($replaces_so_far * 2); // every time you expand one key, offsets need to be shifted with 2 (for the two " chars)
$key = $match[0];
$quotes_before = preg_match_all('/(?<!\\\)"/', substr($out, 0, $position), $m2);
if ($quotes_before % 2) { // not even number of not-escaped quotes, we are in quotes, ignore candidate
continue;
}
$out = substr_replace($out, '"'.$key.'"', $position, strlen($key));
++$replaces_so_far;
}
var_export(json_decode($out, true));
But since google offers this data in RSS feed, i would recommend you to use that one if it works for your usecase, this is just for fun (-:

The JSON feeds from Google always seem to be plagued with problems- formatted incorrectly in some way shape or form. If you switch the feed to RSS you can easily convert it to an array or JSON from the array.
<?php
$contents = file_get_contents('http://www.google.com/finance/company_news?q=aapl&output=rss&start=1&num=1');
// Convert the RSS to an array (probably just use this)
$arr = simplexml_load_string($contents);
// Or if you specifically want JSON
$json = json_encode($arr);
// And back to an array
print_r(json_decode($json));

Related

reversing the value of number after converting using str_pad

I am converting the numbers to be used in API
for example:
$total = 100.5;
$purchaseAmt = str_pad($total, 13, "0", STR_PAD_LEFT);
$formattedPurchaseAmt = str_pad(''.($purchaseAmt*100), 12, "0", STR_PAD_LEFT);
it will output 000000010050
Now, how I can do the opposite, if I have 000000010050 i want to get 100.5
Thanks
I think if you round up your float and format it to always have two numbers after the decimal, you could simply do the opposite of what you do, which is divide by 100
$total = round(100.5, 2);
$purchaseAmt = str_pad($total, 13, "0", STR_PAD_LEFT);
$formattedPurchaseAmt = str_pad(''.($purchaseAmt*100), 12, "0", STR_PAD_LEFT);
#reverting the process
$newTotal = (int)formattedPurchaseAmt / 100; //output: 100.5
Test this code:
echo number_format($formattedPurchaseAmt/100, 1, '.');
Maybe it does not suits all cases, so test it well before to use in production.

how to check if a number starts with two allowed numbers php?

How to validate that the numeric string starts the first two numbers only with the following list allowed:
01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, and 30.
$number = 1234567890;
if(ctype_digit($number)){
if (strlen($number) <= 10) {
echo "The registered number is correct";
} else {
echo "The number must have 10 digits";
}
} else {
echo "Number must be digit / numeric only.";
}
What I want to add to this functionality is to validate that said number that is stored in $number must start with the first two numbers that are in the list in order to continue.
Another way is to temporarily cast the number into a string and make use of the regular expression of ^([01]\d|2[0-4]|30) to check whether the number string starts with the specific values you mentioned in the above. You can use PHP's preg_match function to help check whether the string matches the regular expression.
So your code becomes:
if(ctype_digit($number)){
if (strlen($number) <= 10 and preg_match('/^([01]\d|2[0-4]|30)/', (string)$number)) {
echo "The registered number is correct";
} else {
echo "The number must have 10 digits or it begins with incorrect values";
}
} else {
echo "Number must be digit / numeric only.";
}
Regular expression explanation:
^: Matches the pattern from the start of the string
[01]\d: Matches 00, 01, 02, ... , 10, 11, ... , 19
2[0-4]: Matches 20, 21, ... , 24
30: Matches 30
([01]\d|2[0-4]|30): Match either [01]\d, 2[0-4] or 30
preg_match function returns 1 if the regular expression pattern is matched successfully.
If your allowed starting numbers are stored in an array, you could extract the 2 first numbers from $number using substr() and then check with in_array().
You can use regular expressions for this, but something to note: a number starting with a 0 will be interpreted as octal and converted to base 10. To avoid this, you must quote the number:
$number = '055231';
// Fill the list of allowed numbers
$allowed = array_map(function($number) {
// Fill each element to length 2 with '0' to the left
return str_pad($number, 2, '0', STR_PAD_LEFT);
}, range(1, 24));
$allowed[] = 30;
if (preg_match('/^\d{2,10}$/', $number)) {
// At this point we know it contains only numbers
// Extract first two characters
$start = substr($number, 0, 2);
// Check if it's in the allowed list
if (in_array($start, $allowed)) {
echo "The registered number is correct";
} else {
echo "The number is incorrect";
}
} else {
echo "Number must be digit / numeric only.";
}
Regex breakdown:
^ and $ are delimiters. The string must match exactly.
\d. Digit character class. Matches only numbers.
{2,10}. Previous group must be of length between 2 and 10 (since it must start with two digits).
Function reference:
range
array_map
str_pad
in_array
substr
A possible alternate solution could be to hardcode the valid prefixes and check if the first 2 characters of the string is one of them using array_key_exists:
$valid_prefixes = array_fill_keys(array("01", "02", "03", "04", "05",
"06", "07", "08", "09", "10",
"11", "12", "13", "14", "15",
"16", "17", "18", "19", "20",
"21", "22", "23", "24", "30"), true);
function starts_with_valid_prefix($str) {
global $valid_prefixes;
return strlen($str) >= 2 && array_key_exists(substr($str, 0, 2), $valid_prefixes);
}

Issue in JSON Decode

Hi I am getting a weird issue in JSON decode, JSON is getting decoded correctly locally, but on my server the json_decode function returns NULL.
This is the JSON that I am posting from my test page:
[
{
"pictureTaken": 0,
"unit_id": 20192,
"id": 2,
"deficiency_id": 155,
"last_modifier_id": 4,
"comments": "Living room",
"level": 3,
"location": "Living room",
"property_id": 26,
"inspectable_item_id": 44,
"building_id": -769876698
}
]
now when I do var_dump(json_deocde($_POST['data'], true)); I get NULL response.
when I do echo $_POST['data']; I get:
[ { \"pictureTaken\": 0, \"unit_id\": 20192, \"id\": 2, \"deficiency_id\": 155, \"last_modifier_id\": 4, \"comments\": \"Living room\", \"level\": 3, \"location\": \"Living room\", \"property_id\": 26, \"inspectable_item_id\": 44, \"building_id\": -769876698 } ]
I think due to these \" json_decode is not working, kindly help me in fixing this issue,
Some Server Info:
PHP Version 5.2.17
json version 1.2.1
You have magic quotes enabled on your server. Disable them.
you can always do this :
var_dump(json_deocde(str_replace("\",$_POST['data']), true));
that would remove the slashes from your json string

How would I convert this array to usable javascript array?

I have php code that returns this from a database,
[
{
"Time": "2012-11-27 16:10:35",
"NumPlayers": "1"
},
{
"Time": "2012-11-27 16:24:55",
"NumPlayers": "1"
},
{
"Time": "2012-11-27 16:25:37",
"NumPlayers": "2"
},
{
"Time": "2012-11-27 16:29:33",
"NumPlayers": "2"
The times are MySQL timestamps. I need to get it into this format to use with Highcharts javascript charting.
data: [
[Date.UTC(1970, 9, 9), 0 ],
[Date.UTC(1970, 9, 14), 0.15],
[Date.UTC(1970, 10, 28), 0.35],
[Date.UTC(1970, 11, 12), 0.46],
I'm having trouble figuring out how to loop through the MySQL results and convert the timestamps to javascript Date.UTC objects. I need to place the NumPlayers value after the Date.UTC objects, and output it in the format below. I'm a PHP and javascript noob :\
It should look something like this:
data: [
[Date.UTC(2012, 11, 27, 16, 10, 35), 1],
[Date.UTC(2012, 11, 27, 16, 24, 55), 1],
[Date.UTC(2012, 11, 27, 16, 25, 37), 2],
[Date.UTC(2012, 11, 27, 16, 29, 33), 2],
You should realize that Date.UTC(2012, 11, 27, 16, 10, 35) simply returns the number of milliseconds since the epoch (1356624635000). Therefore, you can just convert your object into UNIX timestamps (times a 1000 since JS works with millisecond timestamps, but PHP works with seconds).
Sample Code
$data = '[{"Time": "2012-11-27 16:10:35", "NumPlayers": "1"}, {"Time": "2012-11-27 16:24:55", "NumPlayers": "1"}]';
// Make sure date is parsed as UTC
date_default_timezone_set("UTC");
// Convert items into the desired format
$mapper = function($item) {
return array(strtotime($item->Time)*1000, $item->NumPlayers);
}
echo json_encode(array_map($mapper, json_decode($data)));
Output
[[1354032635000,"1"],[1354033495000,"1"]]
You seem to be getting straight JSON from your database, which you can always convert into an array:
$data = '[{"Time": "2012-11-27 16:10:35", "NumPlayers": "1"}, {"Time": "2012-11-27 16:24:55", "NumPlayers": "1"}]';
$arrayData = json_decode($data, true);
After which you can simply iterate through the array and print out the contents of the array in the JS format you need. Something like that:
echo 'data: [' . PHP_EOL;
foreach ($arrayData as $item) {
echo '[Date.UTC(';
$tmp = preg_split( '/(-| |\:)/', $item['Time'] );
echo implode(', ', $tmp);
echo '), ' . $item['NumPlayers'] . '],';
echo PHP_EOL;
}
echo PHP_EOL . '];';
You can split the time string into sections using /[^\d]+/g:
var finalData = [], numbers=/[^\d]+/g, item, dateParts, newDate;
for (var i=0, l=data.length; i<l; i++) {
item = data[i];
dateParts = item.Time.split(numbers);
newDate = Date.UTC.apply(Date, dateParts);
finalData.push([newDate, item.NumPlayers]);
}
See also: MDN's documentation on JavaScript regular expressions (/[^\d]/g is a regex).
The best way to provide data to Highcharts is getting data formated from database, so that you don't have to do it on client side.
To do it you just have to change your query to something like the following.
Backend
$query = "SELECT UNIX_TIMESTAMP(date) * 1000 AS 'date', value FROM ...";
Then to send the result to frontend:
echo json_encode($result);
Frontend
To get the result on frontend, this case using jQuery and assuming that url is your url:
$.getJSON(url, function(json) {
// decode json
var data = $.parseJSON(json);
// Then you just have to pass it to your series
});
Or better, store date values in UTC.
var php_data = [
{
"Time": "2012-11-27 16:10:35",
"NumPlayers": "1"
},
{
"Time": "2012-11-27 16:24:55",
"NumPlayers": "1"
}
];
var length = php_data.length;
hc_data = [];
for(var i = 0; i< length; i++){
php_date = new Date(php_data[i]["Time"]);
hc_data.push(
[
Date.UTC(
php_date.getFullYear(),
php_date.getMonth() + 1,
php_date.getDate()
),
php_data[i]["NumPlayers"]
]
);
}
// hc_data is your Array

removing data from regular expression [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Parse a text file containing image data
Here is my code for getting image data from a text file:
while (!feof($fh)) {
$line = fgets($fh);
$lines[] = $line;
$match1 ="/^[0-9]{1,3},[0-9]{1,3}/";
$match2 = "/[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},(?:,?[0-9]{1,3})*(?:\.[0-9]{1,10})?\b/";
$parts = preg_match($match1, $line, $regs);
$parts2 = preg_match($match2, $line, $regs2);
foreach($regs as $key => $lame) {
$lamer[] = $lame;
}
foreach($regs2 as $key => $lame2) {
$lamer2[] = $lame2;
}
}
The first preg_match gets the coords, and second gets the rgba() data.
I'm trying to put this into a javascript array but I am getting this error:
SyntaxError: too many constructor arguments
I assume it's too much data for the javascript array.
Now I am wondering how or if I can skip data in the array, namely the the coords
that have a rgba with 0 alpha, which would mean that I would have to skip both.
I'm also wondering if I should try to combine the two matches into one to see if that would make it easier, but I'm not sure how to do that.
Here's the data I am working with that is a 300x180 image:
41,6: (255,255,255, 0) #FFFFFF00 srgba(255,255,255,0)
42,6: (255,255,255, 0) #FFFFFF00 srgba(255,255,255,0)
90,35: ( 77, 80, 12, 98) #4D500C62 srgba(77,80,12,0.384314)
91,35: ( 95, 99, 13, 78) #5F630D4E srgba(95,99,13,0.305882)
92,35: ( 96, 99, 31, 90) #60631F5A srgba(96,99,31,0.352941)
93,35: (106,110, 14, 68) #6A6E0E44 srgba(106,110,14,0.266667)
94,35: ( 95, 99, 13, 78) #5F630D4E srgba(95,99,13,0.305882)
Use JavaScript RegExp pattern
^(\\d+),(\\d+)[^#]+#.{6}(?!00)[^(]+\\((\\d+),(\\d+),(\\d+),(\\d*(?:\\.\\d*)?)
Check this demo.

Categories