dotink/json

为PHP提供更好的JSON序列化

1.6.0 2023-11-06 13:57 UTC

This package is auto-updated.

Last update: 2024-09-06 16:54:40 UTC


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\Serializejson_encode() 对内置PHP对象进行编码可能会产生不同的结果。

当然,可以扩展这些对象并使用 Json\Serialize,但您需要将此类实例替换为其子类

namespace My;

class DateTime extends \DateTime implements Json\Serializable
{
	use Json\Serialize;
}

为什么存在这样的功能?

传统的PHP json_encode() 函数能够为类/对象提供一种机制,以便它们可以将数据规范化以进行序列化。实现这一机制的是 JsonSerializable 接口和相应的 jsonSerialize() 方法。尽管这种方法解决了许多基本的规范化案例,但它并没有解决许多更高级的案例。

这个库是为了解决一个特定的案例而编写的

  1. 我需要一个方法来使标准的 json_encode 能够序列化对象。
  2. 这些对象的序列化数据取决于它们是否嵌套。
  3. 数据规范化需要执行,并且这些依赖项不能注入到对象中,也许只是为了它们自身的规范化,也不应该这样做。

为什么不使用完全独立的序列化库?

虽然有许多优秀的序列化库可用,但目前将序列化库集成到可能使用 json_encode 的每个地方并不是一个现实的选择。此外,如果没有某种形式的标准化序列化接口,其他库可能仍然会继续使用 json_encode()。参见第1点。