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 2016年

34

JSONオブジェクトを独自のモデルクラスに自動的にマッピングするJsonMapperを構築しました。ネストされた/子オブジェクトで正常に機能します。

とにかくほとんどのクラスプロパティが持っているマッピングのためにdocblockタイプ情報のみに依存します:

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

1
うわー!それはすごいです。
vothaison 2017

OSL3ライセンスについて説明していただけますか?WebサイトでJsonMapperを使用する場合、そのWebサイトのソースコードをリリースする必要がありますか?販売しているデバイスのコードでJsonMapperを使用する場合、そのデバイスのコードはすべてオープンソースである必要がありますか?
EricP

いいえ、JsonMapper自体に加えた変更を公開するだけで済みます。
cweiske

29

あなたはそれを行うことができます-それは応急修理ですが、完全に可能です。カウチベースに物を保管し始めたとき、私たちはしなければなりませんでした。

$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 2014年

2
いいえ、json_decodeは、サブオブジェクトを含むstdclassオブジェクトを作成します。それらを他のものにしたい場合は、上記のように各オブジェクトを処理する必要があります。
ジョンペティット2014年

ありがとう、それは私が想像したものです
J-Rou 2014年

コンストラクターにパラメーターがあるオブジェクトでこのソリューションを使用するのはどうですか。私はそれを動かすことができません。このソリューションをパラメーター付きのカスタムコンストラクターを持つオブジェクトで機能させるために、誰かが私を正しい方向に向けてくれることを望んでいました。
マルコ

私は先に進み、これを関数に組み込みました。それでもサブクラスでは機能しないことに注意してください。gist.github.com/sixpeteunder/2bec86208775f131ce686d42f18d8621
Peter Lenjo

16

J ohannesSchmittの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
IvanYarych18年

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

いいえ、これはPHP5.5.1以降では不可能です。

可能な唯一のことjson_decodeは、StdClassオブジェクトの代わりに連想配列を返すことです。


3

あなたは以下の方法でそれを行うことができます..

<?php
class CatalogProduct
{
    public $product_id;
    public $sku;
    public $name;
    public $set;
    public $type;
    public $category_ids;
    public $website_ids;

    function __construct(array $data) 
    {
        foreach($data as $key => $val)
        {
            if(property_exists(__CLASS__,$key))
            {
                $this->$key =  $val;
            }
        }
    }
}

?>

詳細については、create-custom-class-in-php-from-json-or-arrayをご覧ください。


3

まだ誰もこれについて言及していないことに驚いています。

Symfonyシリアライザーコンポーネントを使用します: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

ゴードンが言うように、それは不可能です。ただし、giveクラスのインスタンスとしてデコードできる文字列を取得する方法を探している場合は、代わりにserializeとunserializeを使用できます。

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と呼びましょう。パブリックメンバーをシリアル化および逆シリアル化する必要があります。これは、Reflectionとレイトスタティックバインディングを使用して可能です。

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のサブセットでもあります)。これは、特定のタイプ(数値、文字列、配列/リスト、オブジェクト/辞書)のみをサポートします。オブジェクトは単なるkey = valueマップであり、配列は順序付きリストです。

したがって、カスタムオブジェクトを一般的な方法で表現する方法はありません。解決策は、プログラムがそれがカスタムオブジェクトであることを認識する構造を定義することです。

次に例を示します。

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

これを使用して、のインスタンスを作成しMyClass、フィールドafooto123およびを設定できます"bar"


6
これは本当かもしれませんが、問題は一般的な方法でオブジェクトを表現することについて尋ねているのではありません。彼は、一方または両方の端で特定のクラスにマップする特定のJSONバッグを持っているようです。この方法で非ジェネリック名前付きクラスの明示的なシリアル化としてJSONを使用できない理由はありません。一般的なソリューションが必要な場合は、実行中に名前を付けることは問題ありませんが、JSON構造について合意された契約を結んでも問題はありません。
DougW 2013年

これは、エンコード側にSerializableを実装し、デコード側に条件文がある場合に機能する可能性があります。適切に編成されていれば、サブクラスでも機能します。
Peter Lenjo

0

私は先に進み、John Petitの答えを関数(要点)として実装しました:

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);  
}

これは私のユースケースでは完璧に機能しました。しかし、YevgeniyAfanasyevの反応は私にも同様に有望であるように思われます。次のように、クラスに追加の「コンストラクター」を持たせることができます。

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.