wikimedia / json-codec
用于将PHP对象序列化和反序列化为JSON的接口
Requires
- php: >=7.4.3
- psr/container: ^1.1.2|^2.0.2
- symfony/polyfill-php81: ^1.27.0
Requires (Dev)
- mediawiki/mediawiki-codesniffer: 43.0.0
- mediawiki/mediawiki-phan-config: 0.14.0
- mediawiki/minus-x: 1.1.1
- ockcyp/covers-validator: 1.6.0
- php-parallel-lint/php-console-highlighter: 1.0.0
- php-parallel-lint/php-parallel-lint: 1.4.0
- phpunit/phpunit: 9.6.16
- wikimedia/assert: ^0.5.1
- wikimedia/update-history: 1.0.1
README
JsonCodec
用于将PHP对象序列化和反序列化为JSON的接口。
有关此库的更多文档可以在mediawiki.org上找到。
使用方法
要使对象可序列化/反序列化为JSON,最简单的方法是使用JsonCodecableTrait
并在你的类中实现两个方法,toJsonArray()
和静态方法newFromJsonArray()
use Wikimedia\JsonCodec\JsonCodecable; class SampleObject implements JsonCodecable { use JsonCodecableTrait; /** @var string */ public string $property; // .... // Implement JsonCodecable using the JsonCodecableTrait /** @inheritDoc */ public function toJsonArray(): array { return [ 'property' => $this->property, ]; } /** @inheritDoc */ public static function newFromJsonArray( array $json ): SampleObject { return new SampleObject( $json['property'] ); } }
该示例的一个稍微复杂版本可以在tests/SampleObject.php
中找到。
如果你的类需要显式管理 - 例如,需要使用工厂服务创建对象实例,你可以直接实现JsonCodecable
use Wikimedia\JsonCodec\JsonCodecable; class ManagedObject implements JsonCodecable { public static function jsonClassCodec( ContainerInterface $serviceContainer ) { $factory = $serviceContainer->get( 'MyObjectFactory' ); return new class( $factory ) implements JsonClassCodec { // ... public function toJsonArray( $obj ): array { // ... } public function newFromJsonArray( string $className, array $json ): ManagedObject { return $this->factory->create( $json[....] ); } }; } }
一个完整的示例可以在tests/ManagedObject.php
中找到。
请注意,由toJsonArray()
返回的数组可以包含其他JsonCodecable
对象,这些对象将被递归序列化。当在反序列化期间调用newFromJsonArray
时,所有这些递归包含的对象都已反序列化回对象。
要序列化对象到JSON,请使用JsonCodec
use Wikimedia\JsonCodec\JsonCodec; $services = ... your global services object, or null ...; $codec = new JsonCodec( $services ); $string_result = $codec->toJsonString( $someComplexValue ); $someComplexValue = $codec->newFromJsonString( $string_result );
在某些情况下,您可能希望将此输出嵌入到另一个上下文中,或者使用非默认的json_encode
选项进行格式化输出。在这些情况下,在编码/解码之前访问返回或接受编码数组的方法的访问可能很有用
$array_result = $codec->toJsonArray( $someComplexValue ); var_export($array_result); // pretty-print $request->jsonResponse( [ 'error': false, 'embedded': $array_result ] ); $someComplexValue = $codec->fromJsonArray( $data['embedded'] );
处理“不可编码”的对象
在某些情况下,您可能能够序列化/反序列化不实现JsonCodecable的第三方对象。这可以通过使用JsonCodec方法::addCodecFor()
来完成,该方法允许JsonCodec
实例的创建者指定用于任意类名的JsonClassCodec
。例如
use Wikimedia\JsonCodec\JsonCodec; $codec = new JsonCodec( ...optional services object... ); $codec->addCodecFor( \DocumentFragment::class, new MyDOMSerializer() ); $string_result = $codec->toJsonString( $someComplexValue );
默认情况下,这是为了提供一个stdClass
对象的序列化器。
如果逐个添加类编解码器不足以满足需求,例如如果您希望支持实现某些替代序列化接口的所有对象,您可以通过覆盖受保护的JsonCodec::codecFor()
方法并返回适当的编解码器来继承JsonCodec
。您的代码应如下所示
class MyCustomJsonCodec extends JsonCodec { protected function codecFor( string $className ): ?JsonClassCodec { $codec = parent::codecFor( $className ); if ($codec === null && is_a($className, MyOwnSerializationType::class, true)) { $codec = new MyCustomSerializer(); // Cache this for future use $this->addCodecFor( $className, $codec ); } return $codec; } }
一个完整的示例可以在tests/AlternateCodec.php
中找到。
更简洁的输出
默认情况下,JsonCodec将适当对象类型的类名嵌入到JSON输出中,以启用可靠的反序列化。然而,在某些应用程序中,需要更简洁的JSON输出。通过为顶层调用::toJsonArray()
和newFromJsonArray()
提供可选的“类提示”并在您的类编解码器中实现::jsonClassHintFor()
方法,您可以在提供的提示与将要添加的信息匹配时抑制JSON中的不必要类型信息。例如
class SampleContainerObject implements JsonCodecable {
use JsonCodecableTrait;
/** @var mixed */
public $contents;
/** @var list<Foo> */
public array $foos;
// ...
// Implement JsonCodecable using the JsonCodecableTrait
/** @inheritDoc */
public function toJsonArray(): array {
return [ 'contents' => $this->contents, 'foos' => $this->foos ];
}
/** @inheritDoc */
public static function newFromJsonArray( array $json ): SampleContainerObject {
return new SampleContainerObject( $json['contents'], $json['foos'] );
}
/** @inheritDoc */
public static function jsonClassHintFor( string $keyName ) {
if ( $keyName === 'contents' ) {
// Hint that the contained value is a SampleObject. It might be!
return SampleObject::class;
} elseif ( $keyName === 'foos' ) {
// A hint with a modifier
return Hint::build( Foo::class, Hint::LIST );
}
return null;
}
}
您可以通过在序列化和反序列化时提供适当的提示来生成简洁的输出
use Wikimedia\JsonCodec\JsonCodec;
$codec = new JsonCodec();
$value = new SampleContainerObject( new SampleObject( 'sample' ), ... );
$string_result = $codec->toJsonString( $value, SampleContainerObject::class );
// $string_result is now:
// {"contents":{"property":"sample"},"foos":[...]}'
// with no explicit type information.
// But we need to provide the same class hint when deserializing:
$value = $codec->newFromJsonString( $string_result, SampleContainerObject::class );
请注意,提供的值是一个提示。如果我们把除了SampleObject
以外的值放入SampleContainerObject
,该值的类型将被嵌入到JSON输出中,但不会破坏序列化/反序列化。
如foos
属性所示,要指示同质的列表或给定类型的数组,可以将Hint::build(...., Hint::LIST)
作为类提示传递。具有给定类型属性值的stdClass
对象可以使用Hint::build(...., Hint::STDCLASS)
进行提示。
完整示例请参阅tests/SampleContainerObject.php
。
Hint::USE_SQUARE
修饰符允许::toJsonArray()
返回一个列表(请参阅array_is_list
),并将该列表编码为JSON数组,使用方括号[]
。
Hint::ALLOW_OBJECT
修饰符确保空对象序列化为{}
。副作用是,在某些情况下,::toJsonArray()
可能返回一个对象值,而不是从方法名暗示的数组值。
USE_SQUARE
和ALLOW_OBJECT
提示是必要的,因为在正常情况下,JsonCodec
会尝试使用花括号{}
编码所有对象值,在编码结果中插入必要的_type_
属性以确保编码的数组永远不会是列表。PHP的json_encode
将为非列表数组使用{}
表示法。如果您不希望在编码结果中添加_type_
属性,则需要指定在模糊情况下是否更喜欢使用[]
表示法(USE_SQUARE
)或{}
表示法(ALLOW_OBJECT
)。
带有提示修饰符的示例请参阅tests/SampleList.php
及其关联的测试用例。
当可以使用超类编解码器实例化各种子类的对象时,可以使用Hint::INHERITED
修饰符。此示例请参阅tests/Pet.php
、tests/Dog.php
和tests/Cat.php
及其在tests/JsonCodecTest.php
中的关联测试用例。
在某些情况下,::jsonClassHintFor()
可能不足以描述JSON的隐式类型;例如,带有标签的联合值或嵌套在非同质数组中或深层的隐式类型对象。对于这些用例,::jsonClassCodec()
方法提供了一个JsonCodecInterface
参数。这允许序列化/反序列化代码手动使用隐式类型对JSON数组的一部分进行编码/解码。更多详细信息请参阅src/JsonCodecInterface.php
中的接口文档,以及完整的示例请参阅tests/TaggedValue.php
。
使用受保护的JsonCodec::isArrayMarked()
、JsonCodec::markArray()
和JsonCodec::unmarkArray()
方法可以进一步自定义类名和类提示的编码。完整示例请参阅tests/ReservedKeyCodec.php
。
运行测试
composer install
composer test
历史
JsonCodec概念首次在MediaWiki 1.36.0版本中引入(请参阅dbdc2a3cd33)。在MediaWiki 1.41开发周期中将其从MediaWiki代码库中分离出来,并作为一个独立的库发布,同时进行了API的更改。