gojsondiff

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2023 License: Apache-2.0 Imports: 11 Imported by: 1

README

go-jsondiff

License

Installation

go get github.com/mrutkows/go-jsondiff

JSON Delta format

The JSON formatted output from a comparison is based upon a simplified methodology described originally here:

Delta (object) types
Added

a value was added, i.e. it was undefined and now has a value.

delta = [ newValue ]

internal representation:

// delta.(type) == *diff.Added
type Added struct {
	postDelta         // postDelta{position} where `position` is an interface with a String() method
                      // "PostPosition() interface"
	Value interface{} // added value (i.e., 'newValue')
	                  // "Value()" interface
	similarityCache
}

Notes:
  • "add" operation has no "preDelta" value, only a "postDelta" value

Deleted

a value was deleted, i.e. it had a value and is now undefined

delta = [ oldValue, 0, 0 ]
Notes:
  • "delete" operation has no "postDelta" value, only a "preDelta" value

Modified

a value was replaced by another value

delta = [ oldValue, newValue ]

Array (with inner changes)

value is an array, and there are nested changes inside its items

delta = {
  _t: 'a',
  index1: innerDelta1,
  index2: innerDelta2,
  index5: innerDelta5
}
Notes:
  • only indices with "inner" deltas are included
  • _t: 'a': this tag indicates the delta applies to an array,
    • if a regular object (or a value type) is found when patching, an error will be thrown

internal representation:

delta.(type) == *diff.Array

Array Moves

an item was moved to a different position in the same array

delta = [ '', destinationIndex, 3]
Notes:
  • '': represents the moved item value (suppressed by default)
  • 3: indicates "array move"
JSON Delta format example
 {
   "arr": [
     0: "arr0",
     1: 21,
     2: {
       "num": 1,
-      "str": "pek3f"
+      "str": "changed"
     },
     3: [
       0: 0,
-      1: "1"
+      1: "changed"
     ]
   ],
   "bool": true,
   "num_float": 39.39,
   "num_int": 13,
   "obj": {
     "arr": [
       0: 17,
       1: "str",
       2: {
-        "str": "eafeb"
+        "str": "changed"
       }
     ],
+    "new": "added",
-    "num": 19,
     "obj": {
-      "num": 14,
+      "num": 9999
-      "str": "efj3"
+      "str": "changed"
     },
     "str": "bcded"
   },
   "str": "abcde"
 }

When you prefer the delta format of jsondiffpatch, add the -f delta option.

jd -f delta one.json another.json

This command shows:

{
  "arr": {
    "2": {
      "str": [
        "pek3f",
        "changed"
      ]
    },
    "3": {
      "1": [
        "1",
        "changed"
      ],
      "_t": "a"
    },
    "_t": "a"
  },
  "obj": {
    "arr": {
      "2": {
        "str": [
          "eafeb",
          "changed"
        ]
      },
      "_t": "a"
    },
    "new": [
      "added"
    ],
    "num": [
      19,
      0,
      0
    ],
    "obj": {
      "num": [
        14,
        9999
      ],
      "str": [
        "efj3",
        "changed"
      ]
    }
  }
}

Credits

This package is based upon a fork of https://github.com/yudai/gojsondiff and includes the LCS algorithm implemented in https://github.com/yudai/golcs.

Creation of this derivative package was necessitated as both these package repositories (and their dependencies) have not been maintained for over 6 years with known bugs and vulnerabilities accumulating.

Copies of the licenses from these originating projects are included here:

Documentation

Overview

Package gojsondiff implements "Diff" that compares two JSON objects and generates Deltas that describes differences between them. The package also provides "Patch" that apply Deltas to a JSON object.

package lcs provides functions to calculate Longest Common Subsequence (LCS) values from two arbitrary arrays.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Added

type Added struct {

	// Values holds the added value
	Value interface{}
	// contains filtered or unexported fields
}

An Added represents a new added field of an object or an array

func NewAdded

func NewAdded(position Position, value interface{}) *Added

NewAdded returns a new Added

func (*Added) PostApply

func (d *Added) PostApply(object interface{}) interface{}

func (Added) PostPosition

func (i Added) PostPosition() Position

func (Added) Similarity

func (cache Added) Similarity() (similarity float64)

type Array

type Array struct {

	// Deltas holds internal Deltas
	Deltas []Delta
	// contains filtered or unexported fields
}

An Array is a Delta that represents an array of JSON

func NewArray

func NewArray(position Position, deltas []Delta) *Array

NewArray returns an Array

func (*Array) PostApply

func (d *Array) PostApply(object interface{}) interface{}

func (Array) PostPosition

func (i Array) PostPosition() Position

func (Array) Similarity

func (cache Array) Similarity() (similarity float64)

type Deleted

type Deleted struct {

	// The value deleted
	Value interface{}
	// contains filtered or unexported fields
}

A "Deleted" type represents deleted field or index of an Object or an Array.

func NewDeleted

func NewDeleted(position Position, value interface{}) *Deleted

NewDeleted returns a Deleted

func (*Deleted) PreApply

func (d *Deleted) PreApply(object interface{}) interface{}

func (Deleted) PrePosition

func (i Deleted) PrePosition() Position

func (Deleted) Similarity

func (d Deleted) Similarity() (similarity float64)

type Delta

type Delta interface {
	// Similarity calculates the similarity of the Delta values.
	// The return value is normalized from 0 to 1,
	// 0 is completely different and 1 is they are same
	Similarity() (similarity float64)
}

A Delta represents an atomic difference between two JSON objects.

type Diff

type Diff interface {
	// Deltas returns Deltas that describe differences between two JSON objects
	Deltas() []Delta
	// Modified returns true if Diff has at least one Delta.
	Modified() bool
}

A Diff holds deltas generated by a Differ

type Differ

type Differ struct {
	// contains filtered or unexported fields
}

A Differ compares JSON objects and applies patches

func New

func New() *Differ

New returns new Differ with default configuration

func (*Differ) ApplyPatch

func (differ *Differ) ApplyPatch(json map[string]interface{}, patch Diff)

ApplyPatch applies a Diff to an JSON object. This method is destructive.

func (*Differ) Compare

func (differ *Differ) Compare(
	left []byte,
	right []byte,
) (Diff, error)

Compare compares two JSON strings as []bytes and return a Diff object.

func (*Differ) CompareArrays

func (differ *Differ) CompareArrays(
	left []interface{},
	right []interface{},
) Diff

CompareArrays compares two JSON arrays as []interface{} and return a Diff object.

func (*Differ) CompareObjects

func (differ *Differ) CompareObjects(
	left map[string]interface{},
	right map[string]interface{},
) Diff

CompareObjects compares two JSON object as map[string]interface{} and return a Diff object.

type Index

type Index int

A Index is a Position with an int value, which means the Delta is in an Array.

func (Index) CompareTo

func (i Index) CompareTo(another Position) bool

func (Index) String

func (i Index) String() (name string)

type IndexPair

type IndexPair struct {
	Left  int
	Right int
}

IndexPair represents an pair of indices in the Left and Right arrays found in the LCS value.

type Lcs

type Lcs interface {
	// Values calculates the LCS value of the two arrays.
	Values() (values []interface{})
	// ValueContext is a context aware version of Values()
	ValuesContext(ctx context.Context) (values []interface{}, err error)
	// IndexPairs calculates paris of indices which have the same value in LCS.
	IndexPairs() (pairs []IndexPair)
	// IndexPairsContext is a context aware version of IndexPairs()
	IndexPairsContext(ctx context.Context) (pairs []IndexPair, err error)
	// Length calculates the length of the LCS.
	Length() (length int)
	// LengthContext is a context aware version of Length()
	LengthContext(ctx context.Context) (length int, err error)
	// Left returns one of the two arrays to be compared.
	Left() (leftValues []interface{})
	// Right returns the other of the two arrays to be compared.
	Right() (rightValues []interface{})
}

interface to calculate the Longest Common Sequences (LCS) of two arrays. see: https://github.com/yudai/golcs (original impl.) see: https://www.geeksforgeeks.org/longest-common-subsequence-of-two-arrays-out-of-which-one-array-consists-of-distinct-elements-only/ TODO: verify how "naive" this impl. is and seek to improve

func NewLCS

func NewLCS(left, right []interface{}) Lcs

New creates a new LCS calculator from two arrays.

type Modified

type Modified struct {

	// The value before modification
	OldValue interface{}

	// The value after modification
	NewValue interface{}
	// contains filtered or unexported fields
}

A Modified represents a field whose value is changed.

func NewModified

func NewModified(position Position, oldValue, newValue interface{}) *Modified

NewModified returns a Modified

func (*Modified) PostApply

func (d *Modified) PostApply(object interface{}) interface{}

func (Modified) PostPosition

func (i Modified) PostPosition() Position

func (Modified) Similarity

func (cache Modified) Similarity() (similarity float64)

type Moved

type Moved struct {

	// The value before moving
	Value interface{}
	// The delta applied after moving (for compatibility)
	Delta interface{}
	// contains filtered or unexported fields
}

A "Moved" type represents field that is moved, which means the index or name is changed. Note that, in this library, assigning a Moved and a Modified to a single position is not allowed. For compatibility with jsondiffpatch, the Moved in this library can hold the old and new value in it.

func NewMoved

func NewMoved(oldPosition Position, newPosition Position, value interface{}, delta Delta) *Moved

func (*Moved) PostApply

func (d *Moved) PostApply(object interface{}) interface{}

func (Moved) PostPosition

func (i Moved) PostPosition() Position

func (*Moved) PreApply

func (d *Moved) PreApply(object interface{}) interface{}

func (Moved) PrePosition

func (i Moved) PrePosition() Position

func (Moved) Similarity

func (cache Moved) Similarity() (similarity float64)

type Name

type Name string

A Name is a Position with a string, which means the delta is in an object.

func (Name) CompareTo

func (n Name) CompareTo(another Position) bool

func (Name) String

func (n Name) String() (name string)

type Object

type Object struct {

	// Deltas holds internal Deltas
	Deltas []Delta
	// contains filtered or unexported fields
}

An Object is a Delta that represents an object of JSON

func NewObject

func NewObject(position Position, deltas []Delta) *Object

NewObject returns an Object

func (*Object) PostApply

func (d *Object) PostApply(object interface{}) interface{}

func (Object) PostPosition

func (i Object) PostPosition() Position

func (Object) Similarity

func (cache Object) Similarity() (similarity float64)

type Position

type Position interface {
	// String returns the position as a string
	String() (name string)

	// CompareTo returns a true if the Position is smaller than another Position.
	// This function is used to sort Positions by the sort package.
	CompareTo(another Position) bool
}

A Position represents the position of a Delta in an object or an array.

type PostDelta

type PostDelta interface {
	// PostPosition returns the Position.
	PostPosition() Position

	// PostApply applies the delta to object.
	PostApply(object interface{}) interface{}
}

A PreDelta is a Delta that has a position of the right side JSON object. Deltas implements this interface should be applies after PreDeltas.

type PreDelta

type PreDelta interface {
	// PrePosition returns the Position.
	PrePosition() Position

	// PreApply applies the delta to object.
	PreApply(object interface{}) interface{}
}

A PreDelta is a Delta that has a position of the left side JSON object. Deltas implements this interface should be applies before PostDeltas.

type TextDiff

type TextDiff struct {
	Modified

	// Diff string
	Diff []diffmatchpatch.Patch
}

A TextDiff represents a Modified with TextDiff between the old and the new values.

func NewTextDiff

func NewTextDiff(position Position, diff []diffmatchpatch.Patch, oldValue, newValue interface{}) *TextDiff

NewTextDiff returns

func (*TextDiff) DiffString

func (d *TextDiff) DiffString() string

func (*TextDiff) PostApply

func (d *TextDiff) PostApply(object interface{}) interface{}

func (TextDiff) PostPosition

func (i TextDiff) PostPosition() Position

func (TextDiff) Similarity

func (cache TextDiff) Similarity() (similarity float64)

type Unmarshaller

type Unmarshaller struct {
}

func NewUnmarshaller

func NewUnmarshaller() *Unmarshaller

func (*Unmarshaller) UnmarshalBytes

func (um *Unmarshaller) UnmarshalBytes(diffBytes []byte) (Diff, error)

func (*Unmarshaller) UnmarshalObject

func (um *Unmarshaller) UnmarshalObject(diffObj map[string]interface{}) (Diff, error)

func (*Unmarshaller) UnmarshalReader

func (um *Unmarshaller) UnmarshalReader(diffReader io.Reader) (Diff, error)

func (*Unmarshaller) UnmarshalString

func (um *Unmarshaller) UnmarshalString(diffString string) (Diff, error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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