root09
@root09

Можно ли спарсить Json с не экранированными кавычками?

Есть API который возвращает json ответ, в этом ответе не экранируются двойные кавычки, например:
{"name": "my "name"", "value": "123456789"}

В итоге json_decode не может его прочитать, ибо json получается невалидный, могу ли я что-то сделать что бы его прочитать? API не мой и исправить его я не могу.
  • Вопрос задан
  • 221 просмотр
Решения вопроса 1
dollar
@dollar
Делай добро и бросай его в воду.
Можно попробовать исправить поломанный JSON.
Примерно так, но это не покрывает 100% случаев из вашего вопроса
<?php
$str = '{"name": "my "name"", "value": "123456789"}';

function fixJSON($str) {
    $len = strlen($str);
    $result = '';
    $space = '';
    $mode = 0;
    for($i=0;$i<$len;$i++){
        $c = $str[$i];
        switch ($mode) {
            case 0: //вне строки
                if ($c == '"') $mode = 1;
                $result .= $c;
                break;
            case 1: //внутри строки
                if ($c == '"') {
                    $mode = 3;
                    $space = $c;
                    break;
                }
                if ($c == '\\') $mode = 2;
                $result .= $c;
                break;
            case 2: //сразу после слеша (игнорим один любой символ)
                $mode = 1;
                $result .= $c;
                break;
            case 3: //была кавычка внутри строки
                if ($c == ' ' || $c == '\n' || $c == '\t' || $c == '\r')
                    $space .= $c;
                elseif ($c == ',' || $c == '}' || $c == ']' || $c == ':') {
                    //вероятно был конец строки (шанс 99%)
                    $mode = 0;
                    $result .= $space.$c;
                } else {
                    //мы остались внутри строки на 100%. Экранируем
                    $mode = 1;
                    $result .= '\\'.$space;
                    $i--; //уходим назад, т.к. вдруг это снова кавычка
                }
                break;
        }
    }
    return $result;
}

echo(fixJSON($str)); //{"name": "my \"name\"", "value": "123456789"}
?>

Но лучше исправить то место, которое приводит к поломке JSON, чтобы оттуда к вам сразу нормальные строки приходили, а не костыли изобретать.

P.S.
Более совершенный и более упоротый вариант
<?php
$str = '{"say": ""my name", hi", "value": "123456789"}';

function fixJSON($str) {
    $len = strlen($str);
    $result = '';
    $space = '';
    $mode = 0;
    for($i=0;$i<$len;$i++){
        $c = $str[$i];
        switch ($mode) {
            case 0: //вне строки
                if ($c == '"') $mode = 1;
                $result .= $c;
                break;
            case 1: //внутри строки
                if ($c == '"') {
                    $mode = 3;
                    $space = $c;
                    break;
                }
                if ($c == '\\') $mode = 2;
                $result .= $c;
                break;
            case 2: //сразу после слеша (игнорим один любой символ)
                $mode = 1;
                $result .= $c;
                break;
            case 3: //была кавычка внутри строки
                if ($c == ' ' || $c == '\n' || $c == '\t' || $c == '\r')
                    $space .= $c;
                elseif ($c == '}' || $c == ']') {
                    //вероятно был конец строки (шанс 99%)
                    $mode = 4;
                    $space .= $c;
                }
                elseif ($c == ',' || $c == ':') {
                    //вероятно был конец строки (шанс 90%)
                    $mode = 5;
                    $space .= $c;
                } else {
                    //мы остались внутри строки на 100%. Экранируем
                    $mode = 1;
                    $result .= '\\'.$space;
                    $i--; //уходим назад, т.к. вдруг это снова кавычка
                }
                break;
            case 4: //после спец символа
                if ($c == ' ' || $c == '\n' || $c == '\t' || $c == '\r')
                    $space .= $c;
                elseif ($c == ',') {
                    //был конец строки с шансом 99.99%
                    $mode = 0;
                    $result .= $space.$c;
                } else {
                    //внтури строки на 100%
                    $mode = 1;
                    $result .= '\\'.$space;
                    $i--;
                }
                break;
            case 5: //после запятой
                if ($c == ' ' || $c == '\n' || $c == '\t' || $c == '\r')
                    $space .= $c;
                elseif (preg_match('/[0-9"-]+/', $c)) {
                    //был конец строки с шансом 99.9%
                    $mode = 0;
                    $result .= $space;
                    $i--;
                } else {
                    //внтури строки на 100%
                    $mode = 1;
                    $result .= '\\'.$space;
                    $i--;
                }
                break;
        }
    }
    if ($mode>2) $result .= $space;
    return $result;
}

echo(fixJSON($str)); //{"say": "\"my name\", hi", "value": "123456789"}
?>
Но всегда можно в строку засунуть другой JSON и никакой конечный фиксер не справится с такой глубиной.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
TommyV888
@TommyV888 Куратор тега PHP
-
Можно регулярным выражением заменить кавычки, но отталкиваться придется от символов json синтаксиса, а значит если они будут в тексте, то будут ошибки. Этот способ подойдет только если вы уверены что там просто текст, или вас устраивает тот процент ошибок, что вы получите.
Ответ написан
Комментировать
sim3x
@sim3x
Нет
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы