swaggest/json-diff

PHP 的 JSON diff/rearrange/patch/pointer 库

v3.11.0 2024-07-02 11:32 UTC

README

这是一个用于在两个 JSON 文档之间查找无序差异的 PHP 实现。

Build Status Scrutinizer Code Quality Code Climate Code Coverage time tracker

目的

  • 为了简化两个 JSON 文件之间的更改审查,您可以使用对重新排列的格式化 JSON 执行的标准 diff 工具。
  • 通过分析原始 JSON 的删除和更改来检测破坏性更改。
  • 为了保持对象集的原有顺序(例如 swagger.json参数 列表)。
  • 创建和应用由 IETF 在 RFC 6902 中指定的 JSON Patches。
  • 创建和应用由 IETF 在 RFC 7386 中指定的 JSON Merge Patches。
  • 通过 JSON Pointer 检索和修改数据。
  • 递归地通过 JSON 值替换。

安装

git clone https://github.com/swaggest/json-diff.git

Composer

安装 PHP Composer

composer require swaggest/json-diff

库使用

JsonDiff

从两个值(originalnew)创建 JsonDiff 对象。

$r = new JsonDiff(json_decode($originalJson), json_decode($newJson));

在构造过程中,JsonDiff 将递归地构建 newrearranged 值,尽可能保持 original 键的顺序。在 original 中缺失的键将以与 new 值中相同的顺序附加到 rearranged 值的末尾。

如果两个值是对象数组,JsonDiff 将尝试在这些对象中找到一个共同的唯一字段,并使用它作为重新排列的标准。您可以使用 JsonDiff::REARRANGE_ARRAYS 选项启用此行为。

$r = new JsonDiff(
    json_decode($originalJson), 
    json_decode($newJson),
    JsonDiff::REARRANGE_ARRAYS
);

可用选项

  • REARRANGE_ARRAYS 是一个选项,用于启用 数组重新排列 以最小化差异。
  • STOP_ON_DIFF 是一个选项,通过在发现差异时停止比较来提高性能。
  • JSON_URI_FRAGMENT_ID 是一个选项,用于使用 URI 片段标识符表示法(例如:"#/c%25d")。如果未设置,则默认为 JSON 字符串表示法(例如:"/c%d")。
  • SKIP_JSON_PATCH 是一个选项,通过不为此差异构建 JsonPatch 来提高性能。
  • SKIP_JSON_MERGE_PATCH 是一个选项,通过不为此差异构建 JSON Merge Patch 值来提高性能。
  • TOLERATE_ASSOCIATIVE_ARRAYS 是一个选项,允许关联数组模仿 JSON 对象(不推荐)。
  • COLLECT_MODIFIED_DIFF 是一个选项,用于启用 getModifiedDiff

选项可以组合使用,例如 JsonDiff::REARRANGE_ARRAYS + JsonDiff::STOP_ON_DIFF

getDiffCnt

返回差异总数

getPatch

返回差异的 JsonPatch

getMergePatch

返回差异的 JSON Merge Patch

getRearranged

返回重新排列的新值,使用原始顺序。

getRemoved

返回原始值的部分删除值。

getRemovedPaths

返回从原始值中删除的 JSON 路径列表。

getRemovedCnt

返回删除的数量。

getAdded

返回新值的部分增加值。

getAddedPaths

返回添加到新值中的 JSON 路径列表。

getAddedCnt

返回增加的数量。

getModifiedOriginal

返回原始值的部分修改值。

getModifiedNew

返回新值的部分修改值。

getModifiedDiff

返回包含原始和新值的路径的 ModifiedPathDiff 列表。

默认不收集,需要 JsonDiff::COLLECT_MODIFIED_DIFF 选项。

getModifiedPaths

返回从原始值到新值修改的 JSON 路径列表。

getModifiedCnt

返回修改的数量。

JsonPatch

import

JSON-解码的数据创建 JsonPatch 实例。

export

JsonPatch 对象创建补丁数据。

op

JsonPatch 添加操作。

apply

将补丁应用到 JSON-解码的数据上。

setFlags

更改默认行为。

可用标志

  • JsonPatch::STRICT_MODE 禁止将空数组转换为对象以创建键。
  • JsonPatch::TOLERATE_ASSOCIATIVE_ARRAYS 允许关联数组模拟 JSON 对象(不推荐)。

JsonPointer

escapeSegment

转义路径段。

splitPath

JSON Pointer 字符串创建未转义段落的数组。

buildPath

从未转义段落的数组创建 JSON Pointer 字符串。

add

将值添加到由段指定的数据路径中。

get

从由段指定的数据路径中获取值。

getByPointer

从由 JSON Pointer 字符串指定的数据路径中获取值。

remove

从由段指定的数据路径中删除值。

JsonMergePatch

apply

将补丁应用到 JSON-解码的数据上。

JsonValueReplace

process

递归地将所有等于 search 值的节点替换为 replace 值。

示例

$originalJson = <<<'JSON'
{
    "key1": [4, 1, 2, 3],
    "key2": 2,
    "key3": {
        "sub0": 0,
        "sub1": "a",
        "sub2": "b"
    },
    "key4": [
        {"a":1, "b":true, "subs": [{"s":1}, {"s":2}, {"s":3}]}, {"a":2, "b":false}, {"a":3}
    ]
}
JSON;

$newJson = <<<'JSON'
{
    "key5": "wat",
    "key1": [5, 1, 2, 3],
    "key4": [
        {"c":false, "a":2}, {"a":1, "b":true, "subs": [{"s":3, "add": true}, {"s":2}, {"s":1}]}, {"c":1, "a":3}
    ],
    "key3": {
        "sub3": 0,
        "sub2": false,
        "sub1": "c"
    }
}
JSON;

$patchJson = <<<'JSON'
[
    {"value":4,"op":"test","path":"/key1/0"},
    {"value":5,"op":"replace","path":"/key1/0"},
    
    {"op":"remove","path":"/key2"},
    
    {"op":"remove","path":"/key3/sub0"},
    
    {"value":"a","op":"test","path":"/key3/sub1"},
    {"value":"c","op":"replace","path":"/key3/sub1"},
    
    {"value":"b","op":"test","path":"/key3/sub2"},
    {"value":false,"op":"replace","path":"/key3/sub2"},
    
    {"value":0,"op":"add","path":"/key3/sub3"},

    {"value":true,"op":"add","path":"/key4/0/subs/2/add"},
    
    {"op":"remove","path":"/key4/1/b"},
    
    {"value":false,"op":"add","path":"/key4/1/c"},
    
    {"value":1,"op":"add","path":"/key4/2/c"},
    
    {"value":"wat","op":"add","path":"/key5"}
]
JSON;

$diff = new JsonDiff(json_decode($originalJson), json_decode($newJson), JsonDiff::REARRANGE_ARRAYS);
$this->assertEquals(json_decode($patchJson), $diff->getPatch()->jsonSerialize());

$original = json_decode($originalJson);
$patch = JsonPatch::import(json_decode($patchJson));
$patch->apply($original);
$this->assertEquals($diff->getRearranged(), $original);

PHP 类作为 JSON 对象

由于魔法方法和其他限制,PHP 类不能可靠地映射到/从 JSON 对象。在 JsonPointer 中支持 PHP 类的对象,但有限制

  • null 等于不存在

数组重新排列

当启用 JsonDiff::REARRANGE_ARRAYS 选项时,数组项按顺序排列以匹配原始数组。

如果数组包含同质对象,并且这些对象具有具有唯一值的公共属性,则数组按与原始数组中具有相同属性值的项的放置位置进行排序。

示例:原始

[{"name": "Alex", "height": 180},{"name": "Joe", "height": 179},{"name": "Jane", "height": 165}]

与新的

[{"name": "Joe", "height": 179},{"name": "Jane", "height": 168},{"name": "Alex", "height": 180}]

将生成一个补丁

[{"value":165,"op":"test","path":"/2/height"},{"value":168,"op":"replace","path":"/2/height"}]

如果找不到合格的索引属性,则根据项的相等性进行重新排列。

示例:原始

{"data": [{"A": 1, "C": [1, 2, 3]}, {"B": 2}]}

与新的

{"data": [{"B": 2}, {"A": 1, "C": [3, 2, 1]}]}

将生成无差异。

CLI 工具

已移至 swaggest/json-cli