Вопрос о преобразовании объекта JSON

Я конвертирую из JSON в объект и из объекта в массив. Это не то, что я ожидал, вы можете мне объяснить?

$json = '{"0" : "a"}';
$obj = json_decode($json);
$a = (array) $obj;
print_r($a);
echo("a0:".$a["0"]."<br>");

$b = array("0" => "b");
print_r($b);
echo("b0:".$b["0"]."<br>");

Вывод здесь:

Array ( [0] => a ) a0:
Array ( [0] => b ) b0:b

Я бы ожидал a0:a в конце первой строки.

Редактировать: после прочтения ответов я расширил код, что сделало поведение более понятным:

//extended example
$json = '{"0" : "a"}';
$obj = json_decode($json);
$a = (array) $obj;
var_export($a);
echo("a0:".$a["0"]."<br>"); //this line does not work, see the answers
echo $obj->{"0"}."<br>";  //works!

$json = '{"x" : "b"}';
$obj = json_decode($json);
$b = (array) $obj;
var_export($b);
echo("bx:".$b["x"]."<br>");

$c = array("1" => "c");
var_export($c);
echo("c1:".$c["1"]."<br>");

$d = array("0" => "d");
var_export($d);
echo("d0:".$d["0"]."<br>");

Вывод расширенного примера:

array ( '0' => 'a', )a0:
a
array ( 'x' => 'b', )bx:b
array ( 1 => 'c', )c1:c
array ( 0 => 'd', )d0:d 

person user89021    schedule 17.05.2010    source источник
comment
это невероятно, но это то, что делает php %) ошибка?   -  person zerkms    schedule 18.05.2010
comment
zerkms: Я рад, что вы можете подтвердить это поведение. После некоторого использования я не ожидаю, что php будет идеальным. Это не его главная сила. Но, может быть, мы сможем понять, что происходит. ОП Карлторвальд - он же   -  person user89021    schedule 18.05.2010
comment
@karlthorwald: Это очень странно, я предполагаю, что это какая-то ошибка. Попробуйте отправить его через PHP.net.   -  person Alix Axel    schedule 18.05.2010


Ответы (5)


Дополнительная информация содержится в этот старый вопрос. Короткая версия заключается в том, что свойства объектов/классов PHP следуют то же соглашение об именах, что и для переменных. Числовое свойство недопустимо для объекта PHP, поэтому нет четкого правила относительно того, что должно происходить при сериализации объекта из другого языка (json/javascript), имеющего числовой ключ. Хотя вам кажется очевидным, что должно произойти с приведенным выше, кто-то с другим предубеждением считает поведение PHP в этом случае вполне допустимым и предпочтительным.

Итак, это своего рода ошибка, но скорее неопределенная область спецификации без четкого ответа, поэтому не ожидайте, что поведение изменится в соответствии с вашими предпочтениями, а если оно изменится, не ожидайте, что это изменение будет постоянным. .

Чтобы решить некоторые проблемы в комментариях, рассмотрите это

header('Content-Type: text/plain');
$json = '{"0" : "a"}';
$obj = json_decode($json);
$a = (array) $obj;
var_dump($a);
var_dump(array(0=>'a'));
var_dump(array('0'=>'a'));

который выведет что-то вроде этого

array(1) {
  ["0"]=>
  string(1) "a"
}
array(1) {
  [0]=>
  string(1) "a"
}
array(1) {
  [0]=>
  string(1) "a"
}

Массив с одним нулевым строковым ключом не является допустимой конструкцией PHP. Если вы попытаетесь создать один PHP, он превратит для вас ноль в int. Когда вы просите PHP выполнить приведение типов, для которого у него нет определения, он в конечном итоге создает массив со строковым ключом (из-за плохо определенных правил относительно того, что здесь должно происходить).

Хотя совершенно очевидно, что это «неправильное» поведение со стороны PHP, определить правильное поведение в языке со слабой типизацией непросто.

person Alan Storm    schedule 17.05.2010
comment
Тем не менее, если вы print_r() $a после того, как он был преобразован в массив, вы можете ясно видеть, что индекс 0 не был удален. - person Alix Axel; 18.05.2010
comment
Ага. согласен с @Alix. объяснение в stackoverflow.com/questions/1869812/ работает для объектов. но из-за того, что мы явно привели объект к массиву, мы ожидаем, что он будет работать обычным образом. даже если новый массив не будет содержать этот искаженный ключ - это также можно объяснить (из-за искаженного свойства объекта). но полуфункциональный массив выглядит очень глючным. - person zerkms; 18.05.2010
comment
Обновленный вопрос для решения других комментариев. В случае $a вы приводите уже недопустимый объект к массиву, поэтому вы вступаете на территорию неопределенного поведения, и все ставки сняты. В случае $b PHP автоматически преобразует 0 в целое число, чтобы создать корректный массив. Я согласен, что это странное поведение, но в конечном итоге вы столкнетесь с подобным странным поведением на любом языке, который избегает строгой типизации. - person Alan Storm; 18.05.2010
comment
да, последнее обновление со сравнением ключей string vs integer проясняет это поведение php. благодаря. - person zerkms; 18.05.2010
comment
Возможно, стоит добавить очевидное исправление: json_decode($json, true). - person Lightness Races in Orbit; 17.06.2011

Вы можете просто получить к нему доступ как к объекту (stdClass), а не к массиву:

$json = '{"0" : "a"}';
$obj = json_decode($json);
print_r($obj);
echo("a0:".$obj->{"0"}."<br>");

Это самый простой способ, поскольку ваш JavaScript изначально был объектом ({}), а не массивом [].

В качестве альтернативы вы можете сделать это

$arr = json_decode($json, true);

Второй необязательный параметр позволяет вывести ассоциативный массив. http://us.php.net/json_decode

person philfreo    schedule 17.05.2010
comment
вы пропустили 3-ю строку, где объект был преобразован в массив. ваш ответ не объясняет, почему мы видим элемент в var_dump(), но не можем получить к нему доступ. вообще вопрос про object->array конверсию а не про то как работать с json_decode - person zerkms; 18.05.2010
comment
Филфрио, спасибо. это помогает мне продолжать кодирование. но сейчас я держу вопрос открытым, и, возможно, кто-то объяснит неожиданное поведение, которое я обнаружил. ОП Карлторвальд - он же - person user89021; 18.05.2010

Зачем ты это делаешь? Знаете ли вы, что вы можете напрямую использовать декодированные значения JSON в виде массива?

$arr = json_decode($json, true);

echo '<pre>';
print_r($arr);
echo '</pre>';
person Alix Axel    schedule 17.05.2010
comment
В любом случае это не оправдывает результаты ;-) Не так ли? - person zerkms; 18.05.2010

Array ( [0] => a ) a0:
Array ( [0] => b ) b0:b

Опять бесполезные print_r атаки PHP!

Первый массив имеет целочисленный ключ 0, потому что приведение (array) пытается превратить его в плоский массив, подобный списку.

Второй массив сохраняет строковый ключ '0' в стиле ассоциативного массива, с которым вы его построили.

Используйте var_export вместо print_r, и вы сможете легче увидеть разницу.

person bobince    schedule 18.05.2010
comment
Очень помогает знание того, что print_r не работает должным образом. Тем не менее var_export показывает строковый ключ (см. расширенный пример в вопросе) - person user89021; 18.05.2010
comment
@zerkms: var_export, на самом деле - person Lightness Races in Orbit; 17.06.2011
comment
@ user89021: ответ Алана Шторма правильно определяет проблему. Промежуточный шаг ваших данных, существующих как объект, имеет проблему с именем свойства 0, которое недопустимо. - person Lightness Races in Orbit; 17.06.2011

Ну, проблема существует только тогда, когда исходный объект имеет свойства, которые не разрешены [то есть свойства, которые являются числовыми]. Это относится не к json_encode/decode, а к любой операции, связанной с преобразованием объектов в массив. Все целочисленные ключи будут недоступны.

http://www.php.net/manual/en/language.types.array.php - где указано, что: Если объект преобразуется в массив, результатом является массив, элементами которого являются свойства объекта. Ключи — это имена переменных-членов с несколькими заметными исключениями: целочисленные свойства недоступны; частные переменные имеют имя класса, предшествующее имени переменной; защищенные переменные имеют '*' перед именем переменной. Эти предшествующие значения имеют нулевые байты с обеих сторон.

person Xnoise    schedule 17.01.2011