steamulo/json-diff

PHP 的 JSON 差分/重新排列/补丁/指针库

v3.10.4.2 2023-07-07 15:46 UTC

This package is auto-updated.

Last update: 2024-09-07 18:33:24 UTC


README

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

Build Status Scrutinizer Code Quality Code Climate Code Coverage time tracker

目的

  • 为了简化两个 JSON 文件之间的更改审查,您可以使用标准 diff 工具在重新排列的格式化 JSON 上进行。
  • 通过分析从原始 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

从两个值(original 和 new)创建 JsonDiff 对象。

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

在构造过程中,JsonDiff 将递归地构建 rearranged 值,尽可能保持 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 是一个选项,通过不为此 diff 构建 JsonPatch 来提高性能。
  • SKIP_JSON_MERGE_PATCH 是一个选项,通过不为此 diff 构建 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

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

示例

$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}]

vs 新

[{"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}]}

vs 新

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

将产生无差异。

CLI 工具

已移动到 swaggest/json-cli