Правильный возврат объекта JSON с помощью встроенной функции JS
Обновленное сообщение
У меня есть решение этой проблемы, которое работает со мной до сих пор (проверено только в Firefox) . Моей главной целью было динамическое обновление графика Flot с помощью Ajax. Мне также нужно динамически обновлять стили осей, и вы можете сделать это с помощью опции Tickformatter. Чтобы избежать проблем, которые у меня были ранее, я просто вставляю желаемую строку возврата в массив PHP и кодирую этот массив в JSON. После вернулся в "Аякс" функция Я создаю новую функцию с помощью конструктора javascript Function() (Ссылка). Я надеюсь, что это кому-то поможет.
Аякс
$.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
$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"'
)
)
);
Я пропускаю массив данных, так как он выглядит похожим. Оба массива объединяются и кодируются в формате JSON.
public function actionSomeControllerWhichCreatesJSONdata() {
$returnArray = array($data, $options);
echo json_encode($returnArray);
return true;
}
Результирующий массив выглядит так, как я опубликовал в нижней части этого поста.
Оригинальное сообщение
Я пытаюсь уже несколько часов, но не могу заставить это работать.
Я есть запрос Ajax, который получает объект JSON в качестве возвращаемого значения при успешном выполнении. Это прекрасно работает до тех пор, пока я не использую функцию JS в своем массиве JSON. Таким образом, мой массив JSON выглядит следующим образом (после json_encode):
[{
"data": [{
{1,2},
{3,4},
}],
"function": "function(){return \"foo bar\";}"
}]
Для того, чтобы избавиться от "' s в строке функции. Я использую str_replace и заменяю строку в кавычках на строку без кавычек. Тогда это выглядит так:
[{
"data": [{
{1,2},
{3,4},
}],
"function": function(){return "foo bar";}
}]
Простой json_encode работает нормально, и моя функция Ajax читает это как объект JSON. После замены строки выясняется, что возвращаемое значение больше не является объектом JSON, а обычной текстовой строкой. Я попытался снова проанализировать строку с помощью jquery.parseJSON(), но это приводит к синтаксической ошибке:
Синтаксическая ошибка: JSON.синтаксический анализ: неожиданное ключевое слово в строке 1 столбца 396 данных JSON
Функция Ajax:
$.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
});
Поэтому при использовании этой функции и установке типа возвращаемого значения "json" она выдает ошибка. Если возвращаемый тип "текст", и я пытаюсь разобрать строку, я получаю ошибку выше. Есть ли какое-либо решение этой проблемы или я делаю что-то совершенно неправильное?
Спасибо за вашу помощь!
ОБНОВЛЕНИЕ Извините, конечно, мои данные JSON недействительны! Как я уже сказал, мой объект JSON создается с помощью json_encode, который, как я полагаю, должен правильно использовать синтаксис. Простой массив после son_encode выглядит следующим образом:
[[{
"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"
}]
И после str_replace Я получаю
[[{
"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"
}]
2 answers
Адено уже указывал, что ваш синтаксис JSON недопустим. Пожалуйста, попробуйте проверить свой JSON с помощью линтера, например http://jsonformatter.curiousconcept.com/
Это немного лучше:
[{"data":[["1","2"],["3","4"]],"function":"function(){return \"foo bar\";}"}]
И еще одно: вы не должны вручную десериализовывать JSON. Все более новые версии jQuery автоматически десериализуют JSON на основе заголовка типа содержимого ответа.
Вы уже установили тип данных в json для запроса ajax. Ответ является JSON и будет де-сериализован. Таким образом, вы можете напрямую использовать
.success: function(response) {
console.log(response.data);
console.log(response.function);
}
Ответ на вопросы/проблему из комментариев:
Теперь вы снова создали недопустимый JSON, потому что "tickFormatter" : function(val, axis){return val.toExponential(2)},
пропускает кавычки вокруг строки функции.
- Прежде чем вставлять строку с помощью str_replace(), вам нужно позаботиться об экранировании и кавычках. Возможно, вам понадобится небольшая вспомогательная функция, которая правильно экранирует строку - чтобы быть допустимым JSON.
- вам нужно использовать str_replace() правильно: не заменяя внешние кавычки, только внутренние
$tickFormatter$
.
Подождите.. я приведу пример:
// 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\";}"}]
Следующий шаг: Выполняется запрос ajax, данные json передаются клиенту, и вы хотите выполнить переданную вами функцию.
Итак, новый вопрос: "Как вызвать/выполнить функцию JavaScript из строки - без использования eval?"
В целом туннелирование - плохая практика, см.: Допустимо ли определять функции в JSON результаты?
В любом случае, есть разные способы сделать это:
Ссылка: http://everythingfrontend.com/posts/studying-javascript-eval.html
- eval() - зло, но работает.
-
SetTimeout()
setTimeout(" function ", 0);
-
Новая функция()
var codeToExecute = "My.Namespace.functionName()"; var tmpFunc = new Function(codeToExecute); tmpFunc();
-
Документ.запись
document.write('<script> JS </script>')
-
URI данных
var s = document.createElement('script'); s.src = 'data:text/javascript,' + encodeURIComponent('alert("lorem ipsum")') document.body.appendChild(s);
JSON не может содержать подобных функций.
Однако вы можете использовать Javascript для внедрения функции в DOM, поэтому вам нужно пропустить часть str_replace
и просто создать элемент <script type="text/javascript"></script>
в DOM и вставить в него свойство data.function
.