Правильный возврат объекта 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"
 }]
Author: Florian Ragossnig, 2015-01-04

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);
    
 3
Author: Jens A. Koch, 2017-05-23 12:12:52

JSON не может содержать подобных функций. Однако вы можете использовать Javascript для внедрения функции в DOM, поэтому вам нужно пропустить часть str_replace и просто создать элемент <script type="text/javascript"></script> в DOM и вставить в него свойство data.function.

 0
Author: Sven, 2015-01-04 14:06:38