PHP 객체를 JSON으로 직렬화


101

그래서 새로운 JsonSerializable Interface를 우연히 발견했을 때 PHP 객체를 JSON으로 직렬화하는 방법에 대한 정보를 얻기 위해 php.net 을 돌아 다녔습니다 . 그것은 단지의 PHP> = 5.4 불구하고, 나는 5.3.x 환경에서 실행하고 있습니다.

이런 종류의 기능은 어떻게 PHP <5.4를 얻었 습니까?

아직 JSON으로 많이 작업하지는 않았지만 애플리케이션에서 API 레이어를 지원하려고 노력하고 있으며 데이터 개체 ( 그렇지 않으면 뷰로 전송 됨 )를 JSON으로 덤프하는 것이 완벽 할 것입니다.

객체를 직접 직렬화하려고하면 빈 JSON 문자열이 반환됩니다. 그것은 json_encode()도대체 대상으로 무엇을 해야할지 모른다고 가정하기 때문 입니다. 객체를 재귀 적으로 배열로 축소 한 다음 인코딩 해야 합니까?


예

$data = new Mf_Data();
$data->foo->bar['hello'] = 'world';

echo json_encode($data) 빈 객체를 생성합니다.

{}

var_dump($data) 그러나 예상대로 작동합니다.

object(Mf_Data)#1 (5) {
  ["_values":"Mf_Data":private]=>
  array(0) {
  }
  ["_children":"Mf_Data":private]=>
  array(1) {
    [0]=>
    array(1) {
      ["foo"]=>
      object(Mf_Data)#2 (5) {
        ["_values":"Mf_Data":private]=>
        array(0) {
        }
        ["_children":"Mf_Data":private]=>
        array(1) {
          [0]=>
          array(1) {
            ["bar"]=>
            object(Mf_Data)#3 (5) {
              ["_values":"Mf_Data":private]=>
              array(1) {
                [0]=>
                array(1) {
                  ["hello"]=>
                  string(5) "world"
                }
              }
              ["_children":"Mf_Data":private]=>
              array(0) {
              }
              ["_parent":"Mf_Data":private]=>
              *RECURSION*
              ["_key":"Mf_Data":private]=>
              string(3) "bar"
              ["_index":"Mf_Data":private]=>
              int(0)
            }
          }
        }
        ["_parent":"Mf_Data":private]=>
        *RECURSION*
        ["_key":"Mf_Data":private]=>
        string(3) "foo"
        ["_index":"Mf_Data":private]=>
        int(0)
      }
    }
  }
  ["_parent":"Mf_Data":private]=>
  NULL
  ["_key":"Mf_Data":private]=>
  NULL
  ["_index":"Mf_Data":private]=>
  int(0)
}

추가

1)

그래서 이것은 toArray()내가 Mf_Data수업을 위해 고안 한 기능입니다 .

public function toArray()
{
    $array = (array) $this;
    array_walk_recursive($array, function (&$property) {
        if ($property instanceof Mf_Data) {
            $property = $property->toArray();
        }
    });
    return $array;
}

그러나 Mf_Data객체에는 부모 ( 포함 ) 객체에 대한 참조도 있으므로 재귀로 인해 실패합니다. _parent참조를 제거하면 매력처럼 작동합니다 .

2)

후속 작업을 위해 내가 사용했던 복잡한 트리 노드 객체를 변환하는 마지막 함수는 다음과 같습니다.

// class name - Mf_Data
// exlcuded properties - $_parent, $_index
public function toArray()
{
    $array = get_object_vars($this);
    unset($array['_parent'], $array['_index']);
    array_walk_recursive($array, function (&$property) {
        if (is_object($property) && method_exists($property, 'toArray')) {
            $property = $property->toArray();
        }
    });
    return $array;
}

삼)

구현을 좀 더 깔끔하게 정리하여 다시 한 번 확인하겠습니다. instanceof검사를 위해 인터페이스를 사용하는 것보다 훨씬 더 깔끔해 보입니다 method_exists()( 하지만 method_exists()크로스 컷 상속 / 구현 ).

사용 unset()도 약간 지저분 해 보였고 논리를 다른 방법으로 리팩토링해야하는 것 같습니다. 그러나이 구현 은 속성 배열 ( 때문에)을array_diff_key 복사 하므로 고려해야 할 사항이 있습니다.

interface ToMapInterface
{

    function toMap();

    function getToMapProperties();

}

class Node implements ToMapInterface
{

    private $index;
    private $parent;
    private $values = array();

    public function toMap()
    {
        $array = $this->getToMapProperties();
        array_walk_recursive($array, function (&$value) {
            if ($value instanceof ToMapInterface) {
                $value = $value->toMap();
            }
        });
        return $array;
    }

    public function getToMapProperties()
    {
        return array_diff_key(get_object_vars($this), array_flip(array(
            'index', 'parent'
        )));
    }

}

4
+1 좋은 질문입니다. 아직이 기능을 몰랐습니다.
— takeshin 2011-07-26

@takeshin-네, 문서 페이지의 편집 날짜가 4 일 전입니다. 나는 그것을보고 기쁘다!
— Dan Lugg 2011

2
이것을 보는 다른 사람들을 참조하기 위해 json_encode는 객체를 잘 처리 할 수 ​​있습니다. 그러나 해당 개체의 공용 멤버 만 인코딩합니다. 따라서 보호 또는 개인 클래스 변수가있는 경우 게시 된 메서드 중 하나 또는 JsonSerializable이 필요합니다.
— Matthew Herbst

@MatthewHerbst 물론입니다. 확실히 오래된 질문은 이제 오래된, 그리고 <5.4은 더 이상 어쨌든 정말 옵션이 아닙니다 (또는 적어도 안)JsonSerializable
— 댄 Lugg

답변:


45

편집 : 현재 2016-09-24이고 PHP 5.4가 2012-03-01 릴리스되었으며 지원이 2015-09-01 종료되었습니다 . 그래도이 답변은 찬성 투표를받는 것 같습니다. 여전히 PHP <5.4를 사용 하는 경우 보안 위험이 발생하고 프로젝트가 중단 됩니다. <5.4에 머물러야하는 설득력있는 이유가 없거나 이미> = 5.4 버전을 사용하고 있다면이 답변을 사용하지 말고 PHP> = 5.4 (또는 최신 버전)를 사용하고 구현하십시오. 하고 JsonSerializable 인터페이스를 하십시오.


예를 들어라는 함수를 정의 getJsonData();하면 배열, stdClass객체 또는 개인 / 보호 된 매개 변수가 아닌 가시 매개 변수가있는 다른 객체를 반환 하고 json_encode($data->getJsonData());. 본질적으로 5.4에서 함수를 구현하되 직접 호출하십시오.

get_object_vars()클래스 내부에서 호출되는 것처럼 개인 / 보호 된 변수에 액세스 할 수있는 것과 같은 것이 작동 합니다.

function getJsonData(){
    $var = get_object_vars($this);
    foreach ($var as &$value) {
        if (is_object($value) && method_exists($value,'getJsonData')) {
            $value = $value->getJsonData();
        }
    }
    return $var;
}

2
감사합니다 @Wrikken-객체, 그 안에 포함 된 객체 ( 가시성 또는 유형에 관계없이 모든 구성원 )를 연관 배열로 줄이거 나 유형 캐스팅 하는 지름길 이 stdClass있습니까? Reflection 의 방향으로 생각하고 있지만 그렇지 않다면 재귀 적으로 수행 할 무언가를 알아낼 것입니다.
— Dan Lugg 2011

반사는 먼 길입니다. 당신이 따라 내부 사용자의 클래스 getJsonData()기능, 당신은 단지 부를 수있는 get_object_vars()더 많은 개체를 찾고 그 결과를 통해, 루프.
— Wrikken 2011

나는 그것을 거의 정리했습니다. 이제 문제는 재귀입니다. 각 개체에는 _parent속성이 있으므로 트리를 루트로 이동할 수 있습니다. 업데이트는 내 편집을 참조하십시오. 이 문제는 이제 내 원본에서 추상화되었으므로 다른 질문을해야합니다.
— Dan Lugg 2011

unset($array['_parent']);걷기 전에 간단한 것이 트릭을해야합니다.
— Wrikken 2011

감사합니다 @Wrikken-컨텍스트 객체 $parent를 사용자 데이터로 전달하여 복잡한 동등성 테스트를 시도하기 시작 했습니다 array_walk_recursive(). 단순함이 아름답습니다! 또한 $array["\0class\0property"]캐스팅을 사용했기 때문에 널 바이트 오염 때문입니다. 으로 전환 할 것 같습니다 get_object_vars().
— Dan Lugg 2011

91

가장 간단한 경우 유형 힌트가 작동합니다.

$json = json_encode( (array)$object );

7
이것은 네임 스페이스와 자동 로더로 작업하는 경우 길고보기 흉한 속성 이름을 제공합니다.
— BetaRide

이것은 정확하고 간결한 최고의 솔루션입니다!
— Sujal Mandal

4
더 깨끗한 속성 이름을 얻는 방법이 있습니까?
— Christoffer 2015

5
왜 소품 이름의 시작 부분에 \ u0000 * \ u0000을 추가합니까?
— Elia Weiss

1
사유 재산에는 쓸모가 없습니다. 여러분 모두 en.wikipedia.org/wiki/Open/closed_principle에 대해 배워야 합니다.
— Fabian Picone

19

json_encode()공용 멤버 변수 만 인코딩합니다. 그래서 당신이 혼자해야 할 때 비공개를 포함하고 싶다면 (다른 사람들이 제안한 것처럼)


8

다음 코드는 리플렉션을 사용하여 작업을 수행합니다. 직렬화하려는 속성에 대한 getter가 있다고 가정합니다.

    <?php

    /**
     * Serialize a simple PHP object into json
     * Should be used for POPO that has getter methods for the relevant properties to serialize
     * A property can be simple or by itself another POPO object
     *
     * Class CleanJsonSerializer
     */
    class CleanJsonSerializer {

    /**
     * Local cache of a property getters per class - optimize reflection code if the same object appears several times
     * @var array
     */
    private $classPropertyGetters = array();

    /**
     * @param mixed $object
     * @return string|false
     */
    public function serialize($object)
    {
        return json_encode($this->serializeInternal($object));
    }

    /**
     * @param $object
     * @return array
     */
    private function serializeInternal($object)
    {
        if (is_array($object)) {
            $result = $this->serializeArray($object);
        } elseif (is_object($object)) {
            $result = $this->serializeObject($object);
        } else {
            $result = $object;
        }
        return $result;
    }

    /**
     * @param $object
     * @return \ReflectionClass
     */
    private function getClassPropertyGetters($object)
    {
        $className = get_class($object);
        if (!isset($this->classPropertyGetters[$className])) {
            $reflector = new \ReflectionClass($className);
            $properties = $reflector->getProperties();
            $getters = array();
            foreach ($properties as $property)
            {
                $name = $property->getName();
                $getter = "get" . ucfirst($name);
                try {
                    $reflector->getMethod($getter);
                    $getters[$name] = $getter;
                } catch (\Exception $e) {
                    // if no getter for a specific property - ignore it
                }
            }
            $this->classPropertyGetters[$className] = $getters;
        }
        return $this->classPropertyGetters[$className];
    }

    /**
     * @param $object
     * @return array
     */
    private function serializeObject($object) {
        $properties = $this->getClassPropertyGetters($object);
        $data = array();
        foreach ($properties as $name => $property)
        {
            $data[$name] = $this->serializeInternal($object->$property());
        }
        return $data;
    }

    /**
     * @param $array
     * @return array
     */
    private function serializeArray($array)
    {
        $result = array();
        foreach ($array as $key => $value) {
            $result[$key] = $this->serializeInternal($value);
        }
        return $result;
    }  
} 

1
나는 지금 당신을 너무 사랑합니다! 베이컨이나 맥주, 컵 케이크를 보내 드릴게요 컵 케이크는 요?
— Jonathan dos Santos

이것은 훌륭한 수업입니다! 보호 오브젝트 항목에서도 작동합니다.
— Roelof Berkepeis


2

개체 유형이 사용자 지정이기 때문에 솔루션에 동의하는 경향이 있습니다. 인코딩 방법 (예 : JSON 또는 콘텐츠 직렬화)을 사용하여 더 작은 세그먼트로 나누고 다른쪽에는 개체를 재구성하는 해당 코드가 있습니다.


2

내 버전 :

json_encode(self::toArray($ob))

이행:

private static function toArray($object) {
    $reflectionClass = new \ReflectionClass($object);

    $properties = $reflectionClass->getProperties();

    $array = [];
    foreach ($properties as $property) {
        $property->setAccessible(true);
        $value = $property->getValue($object);
        if (is_object($value)) {
            $array[$property->getName()] = self::toArray($value);
        } else {
            $array[$property->getName()] = $value;
        }
    }
    return $array;
}

JsonUtils : GitHub


내가 찾던 바로 그것. 사생활 문제를 해결합니다. 간단하고 작습니다.
— Fabian Picone

1

이것을 사용해보십시오, 이것은 나를 위해 잘 작동했습니다.

json_encode(unserialize(serialize($array)));

1

당신의 변수 유형 변경 private에public

이것은 간단하고 더 읽기 쉽습니다.

예를 들면

작동하지 않는;

class A{
   private $var1="valuevar1";
   private $var2="valuevar2";
   public function tojson(){
    return json_encode($this)
   }
}

작동 중입니다.

class A{
   public $var1="valuevar1";
   public $var2="valuevar2";
   public function tojson(){
    return json_encode($this)
   }
}

매우 이상합니다. 하지만 사실입니다.
— Abilogos

0

get 메서드를 사용하여 개체를 배열로 변환하는 멋진 도우미 클래스를 만들었습니다. 속성에 의존하지 않고 메서드 만 사용합니다.

그래서 두 가지 방법을 포함하는 다음 리뷰 객체가 있습니다.

리뷰

  • getAmountReviews : int
  • getReviews : 주석 배열

논평

  • getSubject
  • getDescription

내가 작성한 스크립트는 다음과 같은 속성을 가진 배열로 변환합니다.

    {
      amount_reviews: 21,
      reviews: [
        {
          subject: "In een woord top 1!",
          description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque laoreet lacus quis eros venenatis, sed tincidunt mi rhoncus. Aliquam ut pharetra diam, nec lobortis dolor."
        },
        {
          subject: "En een zwembad 2!",
          description: "Maecenas et aliquet mi, a interdum mauris. Donec in egestas sem. Sed feugiat commodo maximus. Pellentesque porta consectetur commodo. Duis at finibus urna."
        },
        {
          subject: "In een woord top 3!",
          description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque laoreet lacus quis eros venenatis, sed tincidunt mi rhoncus. Aliquam ut pharetra diam, nec lobortis dolor."
        },
        {
          subject: "En een zwembad 4!",
          description: "Maecenas et aliquet mi, a interdum mauris. Donec in egestas sem. Sed feugiat commodo maximus. Pellentesque porta consectetur commodo. Duis at finibus urna."
       },
       {
          subject: "In een woord top 5!",
          description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque laoreet lacus quis eros venenatis, sed tincidunt mi rhoncus. Aliquam ut pharetra diam, nec lobortis dolor."
    }
]}

출처 : 객체를 JSON으로 인코딩 할 수있는 배열로 변환하는 PHP Serializer.

당신이해야 할 일은 출력에 json_encode를 감싸는 것입니다.

스크립트에 대한 몇 가지 정보 :

  • get으로 시작하는 메소드 만 추가됩니다.
  • 개인 메서드는 무시됩니다.
  • 생성자는 무시됩니다.
  • 메서드 이름의 대문자는 밑줄과 소문자로 대체됩니다.

-7

나는 같은 문제에 몇 시간을 보냈다. 변환 할 객체에는 내가 손대지 말아야하는 정의 (API)를 가진 다른 많은 항목이 포함되어 있으므로 느릴 수있는 솔루션을 생각해 냈지만 개발 목적으로 사용하고 있습니다.

이것은 모든 객체를 배열로 변환합니다.

function objToArr($o) {
$s = '<?php
class base {
    public static function __set_state($array) {
        return $array;
    }
}
function __autoload($class) {
    eval("class $class extends base {}");
}
$a = '.var_export($o,true).';
var_export($a);
';
$f = './tmp_'.uniqid().'.php';
file_put_contents($f,$s);
chmod($f,0755);
$r = eval('return '.shell_exec('php -f '.$f).';');
unlink($f);
return $r;
}

이것은 모든 객체를 stdClass로 변환합니다.

class base {
    public static function __set_state($array) {
        return (object)$array;
    }
}
function objToStd($o) {
$s = '<?php
class base {
    public static function __set_state($array) {
        $o = new self;
        foreach($array as $k => $v) $o->$k = $v;
        return $o;
    }
}
function __autoload($class) {
    eval("class $class extends base {}");
}
$a = '.var_export($o,true).';
var_export($a);
';
$f = './tmp_'.uniqid().'.php';
file_put_contents($f,$s);
chmod($f,0755);
$r = eval('return '.shell_exec('php -f '.$f).';');
unlink($f);
return $r;
}

이미 받아 들여진 또 다른 훌륭하고 정확한 답변이 있습니다. 귀하의 답변은 근본적으로 다르거 나 더 효율적이거나 간결한 것을 추가합니까? 난 안 추측
— 야로 슬라브

나는 정직 할 것입니다. 나는 이것이 질문에 전혀 대답하지 않는다고 생각합니다.
— Dan Lugg 2013 년

5
약 6 개월이 지났습니다. 나는 찬성 투표로 인해 여기에 정기적으로 돌아 왔고 향후 방문자를 위해 약간의 수정을했습니다. 나는 여전히 이는 어떻게해야되는 도대체 아무 생각이 없습니다.
— Dan Lugg 2014

unlink($thisAnswer);
— Dan Lugg

사람들은 이해하지 못하는 것에 대해 반대하는 경향이 있습니다. 정확한 해결책이 아닐 수도 있지만 살펴볼 것입니다. 그런 경우에 당신은 반대 투표 전에 설명을 요청합니다.
— Gimali
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.