grinfeld / phpjsonable
简单的对象JSON编码和解码
Requires
- php: >=5.5.4
Requires (Dev)
- phpunit/phpunit: ^4.0
README
PHP jsonable
PHPjsonable 是一个小型 PHP 库,可以将简单的 Java 对象从 JSON 格式解码到对象,也可以将对象编码到 JSON 格式(实际上,它是我的 Java jsonable 库的转换版本,经过轻微修改以适应 PHP)
由于它来自 Java,您会发现一些 Java "术语",例如 InputStream 和 OutputStream。这些对象只是 PHP 中的流/字符串的包装器。(我希望未来我会使用 PSR-7 Http\Message 实现)
实际上,如果您只需要对 PHP 的内置类型(数组、int、string、bool)进行 JSON 序列化,则您可以选择使用内置的 json_encode 和 json_decode。在这种情况下,您不需要我的库 :)
安装
有两种选项
- 通过 composer(见 https://packagist.org.cn/packages/grinfeld/phpjsonable)
- phar - 下载 grinfeld_phpjsonable.phar 并将其包含到您的代码中,并添加 TMLoader::get() 以初始化自动加载
使用 phpjsonable
让我们从几个简单的例子开始:假设您有一个 PHP 数组
$ar = ["hello", "bye", "something else"];
并且您需要将其以 JSON 格式发送到某个远程服务器。
假设您需要将其输出为字符串(以便使用 curl、guzzle 或其他 HTTP 工具发送)
$output = new StringOutputStream(); // creating output wrapper for string
Json::encode($ar, $output);
echo $output->toString();
// output will be: "["hello", "bye", "something else"]"
让我们看一个相同的例子,但当我们需要将数据存储在文件中时
$fp = fopen("some.json", "r+")
$output = new StreamOutputStream($fp); // creating output wrapper for string
Json::encode($ar, $output);
fclose($fp);
echo file_get_contents("some.json");
// file content is the same as in previous example: "["hello", "bye", "something else"]"
实际上,StreamOutputStream 包装了任何具有 fputs 函数的 PHP 流:"php:file://"、"php://memory"、"php://temp" 等。如果您需要自己的流,创建一个实现了 OutputStream 并具有 write($str) 方法的类。
让我们看看当您从某个远程服务器接收响应时的例子
$content = "..." // let's assume you received: "{\"status":"OK"}"
$input = new StringInputStream($content);
$myResult = Json::decode($input);
echo $myResult["status"];
// output: OK
在了解一些基础知识之后,现在我们可以转向更复杂的例子。正如我之前所说的,这个包用于序列化对象,而不是简单的 PHP 结构。让我们看看几个例子
假设,您有一个 Foo 类和 Response 类
namespace myApp;
class Foo {
protected $str;
protected $num;
protected $ar;
public function __construct() {
$this->str = "Hello";
$this->num = 100;
$this->ar = array(1,2,3,4);
}
}
namespace myApp;
class Response {
protected $status;
protected $description;
public getStatus() { return $this->status;}
public getDescription() { return $this->description;}
}
现在,我们将这个类发送到某个远程服务器
$output = new StringOutputStream(); // creating output wrapper for string
Json::encode($ar, $output);
// $output->toString() will return:
// "{"str":"Hello","num":100,"ar":[1,2,3,4]}"
// sending data (by calling $output->toString()) to server (use your prefer http tool)
......
......
......
$response = "...."; // getting response "{\"status":100, "description":"OK"}"
$myResult = Json::decode(new StringInputStream($response));
echo $myResult["status"] . " --- " . $myResult["description"]; // output
正如我们所见,我们的远程服务器接收到了一个漂亮的 JSON 字符串,执行了它应该做的事情,并返回了我们能够读取的响应。如果您确定响应总是以关联数组、序列化数组或任何 PHP 内置类型返回,您可以将 $myResult = Json::decode(new StringInputStream($response)); 替换为 $myResult = json_decode($response)
考虑一下远程服务器需要反序列化的选项,将接收到的数据返回到具有相同属性和它自己的实现方式的 Foo 对象中。我们可以通过发送类名(默认情况下此选项是关闭的)来帮助它。我们需要将配置添加到我们的编码方法中。
$output = new StringOutputStream(); // creating output wrapper for string
Json::encode($ar,
$output, new Configuration(array(Configuration::INCLUDE_CLASS_NAME_PROPERTY => "true")));
// $output->toString() will return:
// "{"str":"Hello","num":100,"ar":[1,2,3,4], "class": "myApp\Foo"}"
// sending data (by calling $output->toString()) to server (use your prefer http tool)
......
......
......
$response = "...."; // getting response "{\"status":100, "description":"OK"}"
$myResult = Json::decode(new StringInputStream($response));
echo $myResult["status"] . " --- " . $myResult["description"]; // output
但是,如果您远程服务器是 Java,并且它需要类名带有点,即 myApp.Foo 而不是 myApp\Foo,这里就是这种情况的解决方案。简单地说,将 new Configuration(array(Configuration::INCLUDE_CLASS_NAME_PROPERTY, "true")) 添加到
new Configuration(array(
Configuration::INCLUDE_CLASS_NAME_PROPERTY => "true",
Configuration::CLASS_TYPE_PROPERTY => LanguageStrategyFactory::LANG_JAVA
))
// it will output Foo request in following way:
// "{"str":"Hello","num":100,"ar":[1,2,3,4], "class": "myApp.Foo"}"
现在,你可能认为你的代码中的Foo类不在同一个包(命名空间等)中,并且你不想将你的代码结构暴露给应用程序外的人。你说得对。问题是,我不知道如何读心并预测你明天、下周和下一年将要编写的代码,所以在这种情况下,你需要做更多的工作:通过实现LanguageStrategy接口创建你自己的类名转换策略。它只有一个你需要实现的方法:className($obj)。它接收对象并返回一个字符串(代表类名)。在开始编码/解码之前,将你的实现添加到LanguageStrategyFactory中:LanguageStrategyFactory::addStrategy(int $type, LanguageStrategy yourStrategy)。记住,前三个选项已经被我的内置策略占用(0 -> PHP,1 -> Java,2 -> .NET)。实际上,你可以使用0到2之间的任何类型来覆盖其中一个。这是你的选择。最后,使用Configuration和你的自定义类型
new Configuration(array(
Configuration::INCLUDE_CLASS_NAME_PROPERTY => "true",
Configuration::CLASS_TYPE_PROPERTY => $YourOwnType
))
// it will output Foo request in following way:
// "{"str":"Hello","num":100,"ar":[1,2,3,4], "class": "myApp.Foo"}"
太好了!但你为什么需要使用如此明显的"类"属性呢?也许,使用"itsnotclass"会更好?是的,这是可能的。只需将额外的属性添加到你的配置中
new Configuration(array(
Configuration::INCLUDE_CLASS_NAME_PROPERTY => "true",
Configuration::CLASS_PROPERTY => "itsnotclass"
))
// it will output Foo request in following way:
// "{"str":"Hello","num":100,"ar":[1,2,3,4], "itsnotclass": "myApp\Foo"}"
好的,在关于"类"和上面主题中的选项的愉快讨论之后,让我们假设我们的远程服务器返回以下响应
$response = "..."; // {\"status":100, "description":"OK", "class": "myApp\Response"}
$myResult = Json::decode(new StringInputStream($response));
echo get_class($myResult); // output: myApp\Response
echo $myResult->getStatus(); // output: 100
echo $myResult->getDescription(); // output: "OK"
就是这样。我希望这很有帮助。
错误、更改请求
请写信给我 github@mikerusoft.com