vcn/pipette

轻松从JSON中提取所需内容。

v1.6.0 2024-02-01 14:58 UTC

README

轻松从JSON中提取所需内容。

快速入门

composer require vcn/pipette

(从examples/目录)

<?php

use Vcn\Pipette\Json;

try {
    $parseInt = function (Json\Value $json) {
        return $json->int();
    };

    $parseColor = function (Json\Value $json) use ($parseInt) {
        $name     = $json->field('name')->string();
        $category = $json->field('category')->string();
        $type     = $json->¿field('type')->¿string();
        $codeRgba = $json->field('code')->field('rgba')->arrayMap($parseInt);
        $codeHex  = $json->field('code')->field('hex')->string();

        return [$name, $category, $type, $codeRgba, $codeHex];
    };

    $source = file_get_contents(__DIR__ . '/colors.json');

    $colors = Json::parse($source)->field('colors')->arrayMap($parseColor);

    print_r(['colors' => $colors]);
} catch (Json\Exception\CantDecode | Json\Exception\AssertionFailed $e) {
    print_r($e->getMessage());
}

用法

Pipette期望你有一段字符串,你强烈怀疑它代表JSON。你可以要求Pipette解析它

<?php

use Vcn\Pipette\Json;

$input = <<<JSON
{
  "id": 672,
  "name": "Jane Doe"
}
JSON;

try {
    $json = Json::parse($input);
} catch (Json\Exception\CantDecode $e) {
    error_log($e);
}

解析可能会失败,但如果成功,你将得到一个Json\Value。它代表它可能解析的所有可能值,并允许你查询该值

<?php

use Vcn\Pipette\Json;

$input = <<<JSON
{
  "a": [1,2,3],
  "b": [4,5,6]
}
JSON;

try {
    $json = Json::parse($input);

    $a = $json->field('a'); // Assert this is an object, assert the field 'a' is present, then retrieve it.
    $b = $json->field('b');

    $as = $a->arrayMap( // Assert the 'a' field is an array.
        function (Json\Value $value) {
            return $value->int(); // Assert each value in that array is a number and return those numbers as an array of ints.
        }
    );
    $bs = $b->arrayMap(
        function (Json\Value $value) {
            return $value->int();
        }
    );

    print_r(array_sum(array_merge($as, $bs))); // 21

} catch (Json\Exception\CantDecode | Json\Exception\AssertionFailed $e) {
    error_log($e);
}

请注意,如果这些断言中的任何一个为假,则会抛出异常。

可选值

许多方法可以用倒问号(¿)前缀。这会导致它们在值为null或对象中不存在字段时也返回null。

¿field的情况下,返回的Json\OptionalValue是一个null安全的变体,其中后续的所有调用都将返回null,如果原始字段是null或不存在。

<?php

use Vcn\Pipette\Json;

$input = <<<JSON
{
  "a": "some string"
}
JSON;

try {
    $json = Json::parse($input);

    // Expect $ to be an object, expect field $.a to be present and expect it to be a string or null:
    $foo = $json->field('a')->¿string();

    // Expect $ to be an object, if $.a is not present return null, otherwise expect field $.a to be a string or null:
    $bar = $json->¿field('a')->¿string();

    print_r([$foo, $bar]);

    // $.a is present but $.a is not an object, therefore this fails:
    $baz = $json->¿field('a')->¿field('b')->¿string();

    print_r($baz);
} catch (Json\Exception\CantDecode | Json\Exception\AssertionFailed $e) {
    error_log($e);
}

联合

如果你期望你的JSON与多个结构之一匹配,你可以使用either

<?php

use Vcn\Pipette\Json;

$inputA = <<<JSON
{
  "a": "some string"
}
JSON;
$inputB = <<<JSON
{
  "b": 42
}
JSON;

try {
    foreach ([$inputA, $inputB] as $input) {
        $json = Json::parse($input);

        // With the first input this parser will match its first composite,
        // with the second input the first composite parser fails and falls back to the second.
        // In practice you will either coerce these different types or construct members of a sealed trait.
        $foo = $json->either(
            function (Json\Value $json) {
                return $json->field('a')->string();
            },
            function (Json\Value $json) {
                return $json->field('b')->int();
            }
        );

        print_r($foo);
    }
} catch (Json\Exception\CantDecode | Json\Exception\AssertionFailed $e) {
    error_log($e);
}

参见examples/huttonsrazor.php中的另一个示例。

超出类型的验证

Pipette还提供了一个基本的接口来挂钩更强大的验证方法。目前它支持通过justinrainbow/json-schema进行JSON Schema验证。

想法是你可以定义一个JsonSchemaRepository作为依赖项,它包含对JsonSchemas的引用。这些模式可以在解析JSON后,但在你使用pipette将其转换为类型数据之前进行验证。

<?php

namespace Vcn\Pipette\Examples;

use JsonSchema\Validator;
use Vcn\Pipette\Json;
use Vcn\Pipette\Json\Validators\JsonSchemaRepository;

// Define a dependency.
// This looks for schema files inside the current directory.
// See the json-schema library to tailor this behaviour.
$validator = new Validator();
$baseUri   = "file://" . __DIR__;
$schemas   = new JsonSchemaRepository($validator, $baseUri);

try {
    // Somewhere that has access to this dependency.
    $parseInt = function (Json\Value $json) {
        return $json->int();
    };

    $parseColor = function (Json\Value $json) use ($parseInt) {
        $name     = $json->field('name')->string();
        $category = $json->field('category')->string();
        $type     = $json->¿field('type')->¿string();
        $codeRgba = $json->field('code')->field('rgba')->arrayMap($parseInt);
        $codeHex  = $json->field('code')->field('hex')->string();

        return [$name, $category, $type, $codeRgba, $codeHex];
    };

    $input  = file_get_contents(__DIR__ . '/colors.json');
    $json   = $schemas->get('/colors.schema.json')->parse($input); // Use the colors.schema.json schema to first validate the JSON before returning.
    $colors = $json->apply($parseColor);

    print_r($colors);

} catch (Json\Exception\CantDecode | Json\Exception\AssertionFailed $e) {
    echo $e->getMessage() . "\n";
}

参见examples/目录中的典型请求解析器设置。

为JSON-like数据使用Pipette

在底层,Pipette简单地包装了由json_decode()返回的数据,并根据你的查询执行临时的验证。这意味着如果你有与json_decode()类型同构的数据,你也可以为这些数据使用这个库

<?php

use Vcn\Pipette\Json;

try {
    $data = (object)[
        'foo'       => 'bar',
        'baz'       => 123,
        'seventeen' => (object)[
            'eighteen',
            'nineteen',
        ]
    ];

    // The object casts are necessary since json_decode produces an stdClass for JSON objects.
    $json = Json::pretend($data);

    $bar = $json->field('foo')->string();

    print_r($bar);
} catch (Json\Exception\AssertionFailed $e) {
    error_log($e);
}

Pipette支持哪些数据?(或php-json如何表示JSON?)

  • 字符串
  • int / float (JSON数字)
  • 布尔值
  • null
  • array (非关联数组,JSON数组)
  • stdClass (JSON对象)

Pipette对任何其他类型的操作行为是未定义的。

小心!

测试

phpspec提供支持。phpspec已配置为收集代码覆盖率信息。这需要xdebugpcov。您也可以禁用代码覆盖率。

# default (using pcov)
php vendor/bin/phpspec run

# xdebug coverage
XDEBUG_MODE=coverage php vendor/bin/phpspec run

# no code coverage
php vendor/bin/phpspec run -c phpspec-nocc.yml