json_diff

package module
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2021 License: Apache-2.0 Imports: 10 Imported by: 0

README

Json-Diff

RFC 6902 的 Go 语言实现

GoDoc

go get -u github.com/520MianXiangDuiXiang520/json-diff

功能:

序列化与反序列化

与官方 json 包的序列化和反序列化不同,官方包序列化需要指定一个 interface{}, 像:

package main

import "json"

func main() {
  jsonStr := "{}"
  var jsonObj interface{}
  node := json.Unmarshal(&jsonObj, []byte(jsonStr))
  // ...
}

这样不方便编辑反序列化后的 json 对象, json-diff 可以将任意的 json 串转换成统一的 JsonNode 类型,并且提供一系列的增删查改方法,方便操作对象:

func ExampleUnmarshal() {
    json := `{
        "A": 2,
        "B": [1, 2, 4],
        "C": {
          "CA": {"CAA": 1}
        }
      }`
    jsonNode := Unmarshal([]byte(json))
    fmt.Println(jsonNode)
}
差异比较

通过对比两个 Json 串,输出他们的差异或者通过差异串得到修改后的 json 串

func ExampleAsDiffs() {
	json1 := `{
        "A": 1,
        "B": [1, 2, 3],
        "C": {
          "CA": 1
        }
      }`
	json2 := `{
        "A": 2,
        "B": [1, 2, 4],
        "C": {
          "CA": {"CAA": 1}
        }
      }`
	res, _ := AsDiffs([]byte(json1), []byte(json2), UseMoveOption, UseCopyOption, UseFullRemoveOption)
	fmt.Println(res)
}
func ExampleMergeDiff() {
	json2 := `{
        "A": 1,
        "B": [1, 2, 3, {"BA": 1}],
        "C": {
          "CA": 1,
          "CB": 2
        }
      }`
	diffs := `[
        {"op": "move", "from": "/A", "path": "/D"},
        {"op": "move", "from": "/B/0", "path": "/B/1"},
        {"op": "move", "from": "/B/2", "path": "/C/CB"}
      ]`
	res, _ := MergeDiff([]byte(json2), []byte(diffs))
	fmt.Println(res)
}
输出格式

输出一个 json 格式的字节数组,类似于:

   [
     { "op": "test", "path": "/a/b/c", "value": "foo" },
     { "op": "remove", "path": "/a/b/c" },
     { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
     { "op": "replace", "path": "/a/b/c", "value": 42 },
     { "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
     { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
   ]

其中数组中的每一项代表一个差异点,格式由 RFC 6902 定义,op 表示差异类型,有六种:

  1. add: 新增
  2. replace: 替换
  3. remove: 删除
  4. move: 移动
  5. copy: 复制
  6. test: 测试

其中 move 和 copy 可以减少差异串的体积,但会增加差异比较的时间, 可以通过修改 AsDiff() 的 options 指定是否开启,options 的选项和用法如下:

  // 返回差异时使用 Copy, 当发现新增的子串出现在原串中时,使用该选项可以将 Add 行为替换为 Copy 行为
  // 以减少差异串的大小,但这需要额外的计算,默认不开启
  UseCopyOption JsonDiffOption = 1 << iota

  // 仅在 UseCopyOption 选项开启时有效,替换前会添加 Test 行为,以确保 Copy 的路径存在
  UseCheckCopyOption

  // 返回差异时使用 Copy, 当发现差异串中两个 Add 和 Remove 的值相等时,会将他们合并为一个 Move 行为
  // 以此减少差异串的大小,默认不开启
  UseMoveOption

  // Remove 时除了返回 path, 还返回删除了的值,默认不开启
  UseFullRemoveOption
相等的依据

对于一个对象,其内部元素的顺序不作为相等判断的依据,如

{
  "a": 1,
  "b": 2,
}

{
  "b": 2,
  "a": 1,
}

被认为是相等的。

对于一个列表,元素顺序则作为判断相等的依据,如:

{
  "a": [1, 2]
}

{
  "a": [2, 1]
}

被认为不相等。

只有一个元素的所有子元素全部相等,他们才相等

原子性

根据 RFC 6092,差异合并应该具有原子性,即列表中有一个差异合并失败,之前的合并全部作废,而 test 类型就用来在合并差异之前检查路径和值是否正确,你可以通过选项开启它,但即便不使用 test,合并也是原子性的。

json-diff 在合并差异前会深拷贝源数据,并使用拷贝的数据做差异合并,一旦发生错误,将会返回 nil, 任何情况下都不会修改原来的数据。

参考

https://github.com/flipkart-incubator/zjsonpatch

Documentation

Overview

Package json_diff : Go language implementation of RFC6902 https://tools.ietf.org/html/rfc6902

Index

Examples

Constants

View Source
const (
	DiffTypeAdd = iota + 1
	DiffTypeRemove
	DiffTypeReplace
	DiffTypeMove
	DiffTypeCopy
	DiffTypeTest
)

RFC6902 https://tools.ietf.org/html/rfc6902

Variables

This section is empty.

Functions

func AsDiffs

func AsDiffs(source, patch []byte, options ...JsonDiffOption) ([]byte, error)

AsDiffs 比较 patch 相比于 source 的差别,返回 json 格式的差异文档。

Example
json1 := `{
        "A": 1,
        "B": [1, 2, 3, {"1": 2}],
        "C": {
          "CA": 1
        }
      }`
json2 := `{
        "A": 2,
        "B": [{"1": 2}, 1, {"1": 2}, 4],
        "C": {
          "CA": {"CAA": 1}
        }
      }`
res, err := AsDiffs([]byte(json1), []byte(json2), UseMoveOption, UseCopyOption, UseFullRemoveOption)
if err != nil {
	log.Fatalln(err)
}
fmt.Println(string(res))
Output:

func DeepCopy

func DeepCopy(src *decode.JsonNode) (*decode.JsonNode, error)

func GetDiffNode added in v0.0.2

func GetDiffNode(sourceJsonNode, patchJsonNode *decode.JsonNode, options ...JsonDiffOption) *decode.JsonNode

GetDiffNode 比较两个 JsonNode 之间的差异,并返回 JsonNode 格式的差异结果

func Marshal deprecated

func Marshal(root *decode.JsonNode) ([]byte, error)

Deprecated: 请使用 decode.Marshal

func MergeDiff

func MergeDiff(source, diff []byte) ([]byte, error)

MergeDiff 根据差异文档 diff 还原 source 的差异

Example
json2 := `{
        "A": 1,
        "B": [1, 2, 3, {"BA": 1}],
        "C": {
          "CA": 1,
          "CB": 2
        }
      }`
diffs := `[
        {"op": "move", "from": "/A", "path": "/D"},
        {"op": "move", "from": "/B/0", "path": "/B/1"},
        {"op": "move", "from": "/B/2", "path": "/C/CB"}
      ]`
res, _ := MergeDiff([]byte(json2), []byte(diffs))
fmt.Println(res)
Output:

func MergeDiffNode added in v0.0.2

func MergeDiffNode(source, diffs *decode.JsonNode) (*decode.JsonNode, error)

MergeDiffNode 将 JsonNode 类型的 diffs 应用于源 source 上,并返回合并后的新 jsonNode 对象 如果 diffs 不合法,第二个参数将会返回 BadDiffsError

func Parse

func Parse(input []byte) (*decode.JsonNode, error)

Parse 于 Unmarshal 无异

func Unmarshal deprecated

func Unmarshal(input []byte) (*decode.JsonNode, error)

Deprecated: 请使用 decode.UnMarshal Unmarshal 将使用 json 编码的数据反序列化为 JsonNode 对象。

Types

type DiffType

type DiffType int

func (DiffType) String added in v0.1.0

func (dt DiffType) String() string

type JsonDiffOption

type JsonDiffOption uint
const (
	// UseCopyOption 返回差异时使用 Copy, 当发现新增的子串出现在原串中时,使用该选项可以将 Add 行为替换为 Copy 行为
	// 以减少差异串的大小,但这需要额外的计算,默认不开启
	UseCopyOption JsonDiffOption = 1 << iota

	// UseCheckCopyOption 仅在 UseCopyOption 选项开启时有效,替换前会添加 Test 行为,以确保 Copy 的路径存在
	UseCheckCopyOption

	// UseMoveOption 返回差异时使用 Copy, 当发现差异串中两个 Add 和 Remove 的值相等时,会将他们合并为一个 Move 行为
	// 以此减少差异串的大小,默认不开启
	UseMoveOption

	// UseFullRemoveOption Remove 时除了返回 path, 还返回删除了的值,默认不开启
	UseFullRemoveOption
)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL