dotink / json
为PHP提供更好的JSON序列化
Requires
- psr/container: ^2.0
Requires (Dev)
- league/container: ^3.3
- phpunit/phpunit: ^8.4
README
将此库视为将您的 jsonSerialize()
方法移动到单独的类的方式,该类可以进行依赖注入,访问父类中的受保护/私有属性和方法,等等。
安装
composer require dotink/json
用法
$json = Json\Serialize($data);
上述函数将使用在 Json\Normalizer
命名空间中注册的可用规范化器(默认情况下)准备您的 $data
。例如,如果您的 $data
是一个数组,它将使用 Json\Normalizer\_Array
规范化器,该规范化器将依次规范化数组中的所有元素。
对象规范化
对象规范化的操作方式相当不同。
使用的规范化器取决于在 Json\Normalizer
命名空间(默认情况下)中是否有相应的规范化器。如果找不到相应的规范化器,它将回退到 Json\Normalizer\_Object
,该规范化器将规范化所有公共属性。
例如,此库中包含一个 Json\Normalizer\DateTime
类
namespace Json\Normalizer; class DateTime extends \Json\Normalizer { public function jsonSerialize() { return $this->format('c'); } }
因此,如果您的对象是 DateTime
类,它将使用该规范化器。
例如比较
Json\Serialize(new DateTime('today'))
输出为
"2019-11-25T00:00:00-08:00"
变为
json_encode(new DateTime('today'))
输出为
"{"date":"2019-11-25 00:00:00.000000","timezone_type":3,"timezone":"America\/Los_Angeles"}"
添加规范化器
要添加自定义对象规范化器,只需创建一个新的规范化器,其完整类名(包括命名空间)以 Json\Normalizer
为前缀。如果您想规范化 My\Library\Acme
,则应创建 Json\Normalizer\My\Library\Acme
namespace Json\Normalizer\My\Library; class Acme extends \Json\Normalizer { public function jsonSerialize() { // // return normalized object // } }
如果我不想使用此库现有的规范化器怎么办?
您可以更改查找规范化器的命名空间
Json\Normalizer::setNamespace('My\Json\Normalizer')
如果我需要额外的依赖项来规范化我的对象怎么办?
您可以注册任何PSR-11兼容的容器来解析/构建您的规范化器
Json\Normalizer::setContainer($container);
如果我在另一个对象中嵌套规范化我的对象怎么办?
您可以使用 $this('nested')
确定规范化器是否嵌套
namespace Json\Normalizer\My\Library; class Acme extends \Json\Normalizer { public function jsonSerialize() { if ($this('nested')) { // // Return nested object's normalization // } else { // // Return non-nested object's normalization // } } }
如果在规范化我的对象时需要访问受保护的/私有属性/方法怎么办?
规范化器将所有实例属性/方法调用代理到底层对象,如果数据/方法不可访问,则使用反射来访问数据/方法,因此调用 $this->protectedProperty
或 $this->privateMethod()
将像规范化器的 jsonSerialize()
方法在类本身上一样工作。
子类会使用父类的规范化器吗?
是的。使用先前的例子 My\Library\AcmeChild
,它扩展了 My\Library\Acme
,将使用 Json\Normalizer\My\Library\Acme
,除非存在 Json\Normalizer\My\Library\AcmeChild
。
我的现有 JsonSerializable
对象会被规范化吗?
是的。如果您的对象实现了 JsonSerializable
,则有一个 Json\Normalizer\JsonSerializable
规范化器,它将规范化该方法的返回值。
如果我想使用标准 json_encode()
规范化对象怎么办?
您可以通过使用 Json\Serialize
特性为对象添加自定义规范化,无论它们是否通过 Json\Serialize()
或 json_encode()
编码
class Acme implements Json\Serializable { use Json\Serialize; }
注意:
Json\Serializable
只是PHP内置的JsonSerializable
的具体替代接口。如果您的对象已经实现了JsonSerializable
,请将现有的jsonSerialize()
方法移动到自定义规范化器。
如果我只想规范化所有可访问的属性怎么办?
您可以在您的类上使用 Json\SerializeAllProperties
而不是 Json\Serialize
class Acme implements Json\Serializable { use Json\SerializeAllProperties; }
如果我有一些不应该规范化的超级秘密属性,它们的名称以 _
开头怎么办?
您可以在您的类上使用 Json\SerializeStandardProperties
而不是 Json\SerializeAllProperties
class Acme implements Json\Serializable { use Json\SerializeStandardProperties; }
如果我只想规范化所有字符串为 "I'm a teapot?" 怎么办?
您可以添加以下类并使其可自动加载。
namespace Json\Normalizer; class _String extends \Json\Normalizer { public function jsonSerialize() { return "I'm a teapot"; } }
如果我想将其序列化为XML呢?
滚蛋(Get out of here)。
注意事项
规范化是通过实现 JsonSerializable
接口并将数据包裹在实现该接口的规范化器中来实现的。由于无法修改内置的PHP类,如 DateTime
,以直接实现 JsonSerializable
接口,因此使用 Json\Serialize
和 json_encode()
对内置PHP对象进行编码可能会产生不同的结果。
当然,可以扩展这些对象并使用 Json\Serialize
,但您需要将此类实例替换为其子类
namespace My; class DateTime extends \DateTime implements Json\Serializable { use Json\Serialize; }
为什么存在这样的功能?
传统的PHP json_encode()
函数能够为类/对象提供一种机制,以便它们可以将数据规范化以进行序列化。实现这一机制的是 JsonSerializable
接口和相应的 jsonSerialize()
方法。尽管这种方法解决了许多基本的规范化案例,但它并没有解决许多更高级的案例。
这个库是为了解决一个特定的案例而编写的
- 我需要一个方法来使标准的
json_encode
能够序列化对象。 - 这些对象的序列化数据取决于它们是否嵌套。
- 数据规范化需要执行,并且这些依赖项不能注入到对象中,也许只是为了它们自身的规范化,也不应该这样做。
为什么不使用完全独立的序列化库?
虽然有许多优秀的序列化库可用,但目前将序列化库集成到可能使用 json_encode
的每个地方并不是一个现实的选择。此外,如果没有某种形式的标准化序列化接口,其他库可能仍然会继续使用 json_encode()
。参见第1点。