json_decode를 사용자 정의 클래스로


답변:


96

자동이 아닙니다. 하지만 구식 경로로 할 수 있습니다.

$data = json_decode($json, true);

$class = new Whatever();
foreach ($data as $key => $value) $class->{$key} = $value;

또는 더 자동으로 만들 수 있습니다.

class Whatever {
    public function set($data) {
        foreach ($data AS $key => $value) $this->{$key} = $value;
    }
}

$class = new Whatever();
$class->set($data);

편집 : 조금 더 좋아지기 :

class JSONObject {
    public function __construct($json = false) {
        if ($json) $this->set(json_decode($json, true));
    }

    public function set($data) {
        foreach ($data AS $key => $value) {
            if (is_array($value)) {
                $sub = new JSONObject;
                $sub->set($value);
                $value = $sub;
            }
            $this->{$key} = $value;
        }
    }
}

// These next steps aren't necessary. I'm just prepping test data.
$data = array(
    "this" => "that",
    "what" => "who",
    "how" => "dy",
    "multi" => array(
        "more" => "stuff"
    )
);
$jsonString = json_encode($data);

// Here's the sweetness.
$class = new JSONObject($jsonString);
print_r($class);

1
당신의 제안처럼 나는 단지 발언하는 것을 그 것이다 (STDClass 또는 변환 된 객체가 아닌) 중첩 된 개체를하지 작업
javier_domenech

35

JSON 객체를 자체 모델 클래스에 자동으로 매핑하기 위해 JsonMapper 를 구축 했습니다 . 중첩 / 자식 개체와 잘 작동합니다.

대부분의 클래스 속성은 어쨌든 매핑을 위해 docblock 유형 정보에만 의존합니다.

<?php
$mapper = new JsonMapper();
$contactObject = $mapper->map(
    json_decode(file_get_contents('http://example.org/contact.json')),
    new Contact()
);
?>

1
와! 놀랍습니다.
vothaison

OSL3 라이선스에 대해 설명해 주시겠습니까? 웹 사이트에서 JsonMapper를 사용하는 경우 해당 웹 사이트의 소스 코드를 공개해야합니까? 내가 판매하는 장치의 코드에서 JsonMapper를 사용하는 경우 해당 장치의 모든 코드가 오픈 소스 여야합니까?
EricP

1
아니요, JsonMapper 자체에 변경 사항을 게시하기 만하면됩니다.
cweiske

29

당신은 그것을 할 수 있습니다-그것은 kludge이지만 완전히 가능합니다. 소파베이스에 물건을 보관하기 시작했을 때해야 했어요.

$stdobj = json_decode($json_encoded_myClassInstance);  //JSON to stdClass
$temp = serialize($stdobj);                   //stdClass to serialized

// Now we reach in and change the class of the serialized object
$temp = preg_replace('@^O:8:"stdClass":@','O:7:"MyClass":',$temp);

// Unserialize and walk away like nothing happend
$myClassInstance = unserialize($temp);   // Presto a php Class 

벤치 마크에서 이것은 모든 클래스 변수를 반복하는 것보다 훨씬 빠릅니다.

주의 사항 : stdClass 이외의 중첩 된 개체에 대해서는 작동하지 않습니다.

편집 : 데이터 소스를 염두에 두십시오. 위험에 대한 세심한 분석 없이는 사용자의 신뢰할 수없는 데이터와 함께이 작업을 수행하지 않는 것이 좋습니다.


1
캡슐화 된 서브 클래스에서 작동합니까? 예를 들어 { "a": {"b":"c"} }의 객체 a가 단순히 연관 배열이 아닌 다른 클래스에 속합니까?
J-Rou

2
아니요, json_decode는 하위 개체를 포함하여 stdclass 개체를 생성합니다. 다른 개체가되도록하려면 위와 같이 각 개체를 kludge해야합니다.
John Pettitt

상상 무엇, 당신을 감사
J-ROU에게

생성자에 매개 변수가있는 객체에이 솔루션을 사용하는 것은 어떻습니까? 작동시킬 수 없습니다. 이 솔루션이 매개 변수가있는 사용자 지정 생성자가있는 개체와 함께 작동하도록하려면 누군가 나를 올바른 방향으로 안내 할 수 있기를 바랍니다.
Marco

나는 계속해서 이것을 함수로 만들었습니다. 여전히 하위 클래스에서는 작동하지 않습니다. gist.github.com/sixpeteunder/2bec86208775f131ce686d42f18d8621
Peter Lenjo

17

J ohannes Schmitt의 Serializer 라이브러리를 사용할 수 있습니다 .

$serializer = JMS\Serializer\SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, 'MyNamespace\MyObject', 'json');

최신 버전의 JMS 직렬 변환기에서 구문은 다음과 같습니다.

$serializer = SerializerBuilder::create()->build();
$object = $serializer->deserialize($jsonData, MyObject::class, 'json');

2
구문은 JMS 시리얼 버전에 의존하지 않고, PHP 버전 - 당신이 사용할 수있는 PHP5.5에서 시작하여 ::class표기 : php.net/manual/en/...
이반 Yarych

4

개체에 대한 래퍼를 만들고 래퍼를 개체 자체처럼 보이게 만들 수 있습니다. 그리고 그것은 다단계 개체와 함께 작동합니다.

<?php
class Obj
{
    public $slave;

    public function __get($key) {
        return property_exists ( $this->slave ,  $key ) ? $this->slave->{$key} : null;
    }

    public function __construct(stdClass $slave)
    {
        $this->slave = $slave;
    }
}

$std = json_decode('{"s3":{"s2":{"s1":777}}}');

$o = new Obj($std);

echo $o->s3->s2->s1; // you will have 777

3

아니요, PHP 5.5.1부터는 불가능합니다.

가능한 유일한 방법 json_decode은 StdClass 객체 대신에 연관 배열 을 반환하는 것입니다.



3

아무도 이것을 언급하지 않은 것에 놀랐습니다.

Symfony Serializer 구성 요소 사용 : https://symfony.com/doc/current/components/serializer.html

객체에서 JSON으로 직렬화 :

use App\Model\Person;

$person = new Person();
$person->setName('foo');
$person->setAge(99);
$person->setSportsperson(false);

$jsonContent = $serializer->serialize($person, 'json');

// $jsonContent contains {"name":"foo","age":99,"sportsperson":false,"createdAt":null}

echo $jsonContent; // or return it in a Response

JSON에서 객체로 역 직렬화 : (이 예에서는 형식의 유연성을 보여주기 위해 XML을 사용합니다)

use App\Model\Person;

$data = <<<EOF
<person>
    <name>foo</name>
    <age>99</age>
    <sportsperson>false</sportsperson>
</person>
EOF;

$person = $serializer->deserialize($data, Person::class, 'xml');

2

반사 사용 :

function json_decode_object(string $json, string $class)
{
    $reflection = new ReflectionClass($class);
    $instance = $reflection->newInstanceWithoutConstructor();
    $json = json_decode($json, true);
    $properties = $reflection->getProperties();
    foreach ($properties as $key => $property) {
        $property->setAccessible(true);
        $property->setValue($instance, $json[$property->getName()]);
    }
    return $instance;
}

1

Gordon이 말했듯이 불가능합니다. 그러나 제공 클래스의 인스턴스로 디코딩 할 수있는 문자열을 얻는 방법을 찾고 있다면 직렬화 및 직렬화 해제를 대신 사용할 수 있습니다 .

class Foo
{

    protected $bar = 'Hello World';

    function getBar() {
        return $this->bar;
    }

}

$string = serialize(new Foo);

$foo = unserialize($string);
echo $foo->getBar();

이것은 질문을 해결하지 못하는 것 같습니다. 그렇다면 몇 가지 설명을 제공해야합니다.
Felix Kling 2011 년

1

이 목적을 위해 추상 기본 클래스를 만든 적이 있습니다. JsonConvertible이라고합시다. 공용 멤버를 직렬화 및 역 직렬화해야합니다. 리플렉션과 후기 정적 바인딩을 사용하면 가능합니다.

abstract class JsonConvertible {
   static function fromJson($json) {
       $result = new static();
       $objJson = json_decode($json);
       $class = new \ReflectionClass($result);
       $publicProps = $class->getProperties(\ReflectionProperty::IS_PUBLIC);
       foreach ($publicProps as $prop) {
            $propName = $prop->name;
            if (isset($objJson->$propName) {
                $prop->setValue($result, $objJson->$propName);
            }
            else {
                $prop->setValue($result, null);
            }
       }
       return $result;
   }
   function toJson() {
      return json_encode($this);
   }
} 

class MyClass extends JsonConvertible {
   public $name;
   public $whatever;
}
$mine = MyClass::fromJson('{"name": "My Name", "whatever": "Whatever"}');
echo $mine->toJson();

기억에서, 그래서 아마 완벽하지 않을 것입니다. 또한 정적 속성을 제외해야하며 파생 클래스가 json으로 /에서 직렬화 될 때 일부 속성을 무시할 수있는 기회를 제공 할 수 있습니다. 그럼에도 불구하고 당신이 아이디어를 얻길 바랍니다.


0

JSON은 숫자, 문자열, 배열 / 목록, 객체 / 딕셔너리와 같은 특정 유형 만 지원하는 다양한 프로그래밍 언어 (JavaScript의 하위 집합이기도 함)간에 데이터를 전송하는 간단한 프로토콜입니다. 객체는 단지 키 = 값 맵이고 배열은 정렬 된 목록입니다.

따라서 일반적인 방식으로 사용자 지정 개체를 표현할 수있는 방법이 없습니다. 해결책은 프로그램이 사용자 지정 개체라는 것을 알 수있는 구조를 정의하는 것입니다.

예를 들면 다음과 같습니다.

{ "cls": "MyClass", fields: { "a": 123, "foo": "bar" } }

이것은의 인스턴스를 생성하는 데 사용할 수 MyClass와 필드를 설정 a하고 foo123"bar".


6
이것은 사실 일 수 있지만 일반적인 방식으로 객체를 표현하는 것에 대한 질문은 아닙니다. 한쪽 또는 양쪽 끝에서 특정 클래스에 매핑되는 특정 JSON 백을 가지고있는 것 같습니다. 이런 방식으로 비 제네릭 명명 된 클래스의 명시 적 직렬화로 JSON을 사용할 수없는 이유가 없습니다. 일반적인 솔루션을 원하면 이름을 지정하는 것이 좋지만 JSON 구조에 대한 계약에 동의하는 것도 문제가 아닙니다.
DougW 2013 년

인코딩 끝에 Serializable을 구현하고 디코딩 끝에 조건이있는 경우 작동 할 수 있습니다. 제대로 구성되면 하위 클래스로도 작업 할 수 있습니다.
Peter Lenjo

0

나는 계속해서 John Petit의 대답을 함수 ( gist )로 구현했습니다.

function json_decode_to(string $json, string $class = stdClass::class, int $depth = 512, int $options = 0)
{
    $stdObj = json_decode($json, false, $depth, $options);
    if ($class === stdClass::class) return $stdObj;

    $count = strlen($class);
    $temp = serialize($stdObj);
    $temp = preg_replace("@^O:8:\"stdClass\":@", "O:$count:\"$class\":", $temp);
    return unserialize($temp);  
}

이것은 내 사용 사례에 완벽하게 작동했습니다. 그러나 Yevgeniy Afanasyev의 반응 은 나에게도 똑같이 유망 해 보입니다. 다음과 같이 클래스에 추가 "생성자"가있을 수 있습니다.

public static function withJson(string $json) {
    $instance = new static();
    // Do your thing
    return $instance;
}

이것은 또한 이 답변에서 영감을 얻었습니다 .


-1

가장 간단한 방법은 다음과 같습니다.

function mapJSON($json, $class){
$decoded_object = json_decode($json);
   foreach ($decoded_object as $key => $value) {
            $class->$key = $value;
   }
   return $class;}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.