Come accedere alle proprietà degli oggetti con nomi come interi?


87

Sto usando json_decode()qualcosa come:

$myVar = json_decode($data)

Il che mi dà un output come questo:

[highlighting] => stdClass Object
        (
            [448364] => stdClass Object
                (
                    [Data] => Array
                        (
                            [0] => Tax amount liability is ....... 

Voglio accedere al valore della stringa nella chiave [0]. Quando provo a fare qualcosa come:

print $myVar->highlighting->448364->Data->0;

Ottengo questo errore:

Errore di analisi: errore di sintassi, T_DNUMBER imprevisto

I due numeri / interi sembra esserci un problema.



1
@FelixKling: Anch'io ho fatto CV, ma in realtà si scopre che non è un idiota: fa la differenza se il nome della proprietà inizia con un numero o è tutto numeri !
Jon

@ Jon: Mmmh, interessante ... avrei dovuto fare un test prima immagino. Grazie per avermi fatto sapere!
Felix Kling

Risposte:


288

Aggiornato per PHP 7.2

PHP 7.2 ha introdotto una modifica comportamentale alla conversione delle chiavi numeriche nei cast di oggetti e array , che risolve questa particolare incoerenza e fa sì che tutti i seguenti esempi si comportino come previsto.

Una cosa in meno su cui essere confusi!


Risposta originale (si applica alle versioni precedenti alla 7.2.0)

PHP ha la sua parte di vicoli bui in cui davvero non vuoi trovarti. Le proprietà degli oggetti con nomi che sono numeri è una di queste ...

Quello che non ti hanno mai detto

Fatto # 1: non è possibile accedere facilmente a proprietà con nomi che non sono nomi di variabili legali

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->123foo; // error

Fatto # 2: È possibile accedere a tali proprietà con la sintassi parentesi graffa

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->{'123foo'}; // OK!

Fatto # 3: ma non se il nome della proprietà è composto da sole cifre!

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->{'123foo'}; // OK!
echo $o->{'123'}; // error!

Esempio dal vivo .

Fatto # 4: beh, a meno che l'oggetto non provenga da un array in primo luogo.

$a = array('123' => '123');
$o1 = (object)$a;
$o2 = new stdClass;
$o2->{'123'} = '123'; // setting property is OK

echo $o1->{'123'}; // error!
echo $o2->{'123'}; // works... WTF?

Esempio dal vivo .

Abbastanza intuitivo, non sei d'accordo?

Cosa puoi fare

Opzione n. 1: fallo manualmente

L'approccio più pratico è semplicemente eseguire il cast dell'oggetto che ti interessa in un array, che ti consentirà di accedere alle proprietà:

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
$a = (array)$o;
echo $o->{'123'}; // error!
echo $a['123']; // OK!

Sfortunatamente, questo non funziona in modo ricorsivo. Quindi nel tuo caso dovresti fare qualcosa come:

$highlighting = (array)$myVar->highlighting;
$data = (array)$highlighting['448364']->Data;
$value = $data['0']; // at last!

Opzione n. 2: l'opzione nucleare

Un approccio alternativo sarebbe scrivere una funzione che converte gli oggetti in array in modo ricorsivo:

function recursive_cast_to_array($o) {
    $a = (array)$o;
    foreach ($a as &$value) {
        if (is_object($value)) {
            $value = recursive_cast_to_array($value);
        }
    }

    return $a;
}

$arr = recursive_cast_to_array($myVar);
$value = $arr['highlighting']['448364']['Data']['0'];

Tuttavia, non sono convinto che questa sia un'opzione migliore su tutta la linea perché invierà inutilmente agli array tutte le proprietà che non ti interessano così come quelle che sei.

Opzione n. 3: giocare in modo intelligente

Un'alternativa all'opzione precedente consiste nell'usare le funzioni JSON integrate:

$arr = json_decode(json_encode($myVar), true);
$value = $arr['highlighting']['448364']['Data']['0'];

Le funzioni JSON eseguono utilmente una conversione ricorsiva in array senza la necessità di definire alcuna funzione esterna. Per quanto desiderabile sembri, ha lo svantaggio "nucleare" dell'opzione # 2 e inoltre lo svantaggio che se ci sono stringhe all'interno del tuo oggetto, quelle stringhe devono essere codificate in UTF-8 (questo è un requisito di json_encode).


È successo anche per risolvere il mio problema! stackoverflow.com/questions/4643894/...
Bossliaw

Jon, grazie per avermi salvato. Il mio problema però era diverso (immagino che sia davvero nella parte "quello che non ti hanno mai detto"). L'oggetto DateTime recuperato dal DB sembra OK ma se accedo a una qualsiasi delle sue proprietà, come ->dateo ->timezone, nullviene restituito solo . Ho notato che se ho var_dumped l'oggetto prima di utilizzare queste proprietà, vengono restituiti i valori corretti. La clonazione non risolve questo problema, quindi immagino che abbia davvero qualcosa a che fare con l'accesso che var_dumpfa ... Poi ho visto la tua Opzione # 1 e voilá, accedervi come un array ( $objCastAsArray['date']) ha funzionato a meraviglia .
Armfoot

1
Fatto # 0 : il casting di array in oggetti non dovrebbe avere alcun senso puzzolente in primo luogo. Fatto n. 1 al fatto n. 3: non necessario.
Pacerier

4
@ Pacerier: sono d'accordo sul fatto che sia alquanto discutibile, ma in alcune situazioni può avere perfettamente senso. Ad ogni modo, poiché è documentato nel manuale di funzionare in questo modo, le nostre opinioni personali non contano davvero.
Jon

Un'alternativa all'opzione # 3 che non richiede UTF-8 sarebbe$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
OscarJ

10

Volevo solo aggiungere alla spiegazione eloquente di Jon il motivo per cui questo fallisce. È tutto perché durante la creazione di un array, php converte le chiavi in ​​numeri interi, se possibile, il che causa problemi di ricerca su array che sono stati convertiti in oggetti, semplicemente perché la chiave numerica viene preservata. Ciò è problematico perché tutte le opzioni di accesso alle proprietà prevedono o vengono convertite in stringhe. Puoi confermarlo facendo quanto segue:

$arr = array('123' => 'abc');
$obj = (object) $arr;
$obj->{'123'} = 'abc';
print_r( $obj );

Che produrrebbe:

stdClass Object ( 
  [123] => 'abc', 
  [123] => 'abc'
)

Quindi l'oggetto ha due chiavi di proprietà, una numerica (a cui non è possibile accedere) e una basata su stringa. Questo è il motivo per cui Jon's #Fact 4funziona, perché impostando la proprietà usando le parentesi graffe significa che definisci sempre una chiave basata su stringa, piuttosto che numerica.

Prendendo la soluzione di Jon, ma capovolgendola, puoi generare un oggetto dal tuo array che ha sempre chiavi basate su stringa procedendo come segue:

$obj = json_decode(json_encode($arr));

D'ora in poi puoi utilizzare uno dei seguenti perché l'accesso in questo modo converte sempre il valore all'interno della parentesi graffa in una stringa:

$obj->{123};
$obj->{'123'};

Buon vecchio PHP illogico ...


1

Se un oggetto inizia con @like:

SimpleXMLElement Object (
    [@attributes] => Array (
        [href] => qwertyuiop.html
        [id] => html21
        [media-type] => application/xhtml+xml
    )
)

Devi usare:

print_r($parent_object->attributes());

perché $parent_object->{'@attributes'}o $parent_object['@attributes']non funzionerà.


3 anni dopo e questo aiuta ancora le persone, grazie! Sebbene la tua risposta risolva il mio problema, manca di una spiegazione. Qualcuno è in grado di spiegare il motivo dietro questo?
Arbiter

1

Avevo copiato questa funzione dalla rete. Se funziona come dice ("Funzione per convertire oggetti stdClass in array multidimensionali"), prova quanto segue:

<?php

    function objectToArray($d) {
        if (is_object($d)) {
            // Gets the properties of the given object
            // with get_object_vars function
            $d = get_object_vars($d);
        }

        if (is_array($d)) {
            /*
            * Return array converted to object
            * Using __FUNCTION__ (Magic constant)
            * for recursive call
            */
            return array_map(__FUNCTION__, $d);
        }
        else {
            // Return array
            return $d;
        }
    }

?>
  • prima passa il tuo array a objectToArrayfunction
  • quindi prendi il valore di ritorno
  • eco [highlighting][448364][Data][0]

Fonte: PHP stdClass su Array e Array su stdClass


1

Un'ultima alternativa alla risposta completa di Jon:

Usa semplicemente json_decode () con il secondo parametro impostato su true .

$array = json_decode($url, true);

Questo quindi restituisce un array associativo piuttosto che un oggetto, quindi non è necessario convertire dopo il fatto.

Potrebbe non essere adatto a tutte le applicazioni, ma mi ha davvero aiutato a fare facilmente riferimento a una proprietà dell'oggetto originale.

La soluzione è stata trovata in questo tutorial: http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

Saluti


1

Per PHP 7

Accesso alle proprietà degli oggetti che hanno numeri come nome della proprietà. Principalmente necessario dopo aver lanciato l'array sull'oggetto.

    $arr = [2,3,7];
    $o = (object) $arr;

    $t = "1";
    $t2 = 1;
    $t3 = (1);

    echo $o->{1};      // 3
    echo $o->{'1'};   // 3
    echo $o->$t;        // 3
    echo $o->$t2;       // 3
    echo $o->$t3;       // 3

    echo $o->1;       // error
    echo $o->(1);      // error

0

Temo che non ti sia permesso nominare oggetti che iniziano con numeri. Rinomina il primo "448364" che inizia con una lettera.

Il secondo è un array, a cui si accede tra parentesi in questo modo:

print myVar->highlighting->test_448364->Data[0]

anziché


Non posso cambiarlo. L'output viene restituito da un'applicazione di cui non ho alcun controllo.
avinash shah
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.