grinfeld/phpjsonable

简单的对象JSON编码和解码

1.0.5 2016-10-26 21:01 UTC

This package is auto-updated.

Last update: 2024-09-29 04:06:02 UTC


README

Build Status

PHP jsonable

PHPjsonable 是一个小型 PHP 库,可以将简单的 Java 对象从 JSON 格式解码到对象,也可以将对象编码到 JSON 格式(实际上,它是我的 Java jsonable 库的转换版本,经过轻微修改以适应 PHP)

由于它来自 Java,您会发现一些 Java "术语",例如 InputStreamOutputStream。这些对象只是 PHP 中的流/字符串的包装器。(我希望未来我会使用 PSR-7 Http\Message 实现)

实际上,如果您只需要对 PHP 的内置类型(数组、int、string、bool)进行 JSON 序列化,则您可以选择使用内置的 json_encodejson_decode。在这种情况下,您不需要我的库 :)

安装

有两种选项

  1. 通过 composer(见 https://packagist.org.cn/packages/grinfeld/phpjsonable
  2. 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