gojsondiff.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. // Package gojsondiff implements "Diff" that compares two JSON objects and
  2. // generates Deltas that describes differences between them. The package also
  3. // provides "Patch" that apply Deltas to a JSON object.
  4. package gojsondiff
  5. import (
  6. "container/list"
  7. "encoding/json"
  8. "reflect"
  9. "sort"
  10. dmp "github.com/sergi/go-diff/diffmatchpatch"
  11. "github.com/yudai/golcs"
  12. )
  13. // A Diff holds deltas generated by a Differ
  14. type Diff interface {
  15. // Deltas returns Deltas that describe differences between two JSON objects
  16. Deltas() []Delta
  17. // Modified returnes true if Diff has at least one Delta.
  18. Modified() bool
  19. }
  20. type diff struct {
  21. deltas []Delta
  22. }
  23. func (diff *diff) Deltas() []Delta {
  24. return diff.deltas
  25. }
  26. func (diff *diff) Modified() bool {
  27. return len(diff.deltas) > 0
  28. }
  29. // A Differ conmapres JSON objects and apply patches
  30. type Differ struct {
  31. textDiffMinimumLength int
  32. }
  33. // New returns new Differ with default configuration
  34. func New() *Differ {
  35. return &Differ{
  36. textDiffMinimumLength: 30,
  37. }
  38. }
  39. // Compare compares two JSON strings as []bytes and return a Diff object.
  40. func (differ *Differ) Compare(
  41. left []byte,
  42. right []byte,
  43. ) (Diff, error) {
  44. var leftMap, rightMap map[string]interface{}
  45. err := json.Unmarshal(left, &leftMap)
  46. if err != nil {
  47. return nil, err
  48. }
  49. err = json.Unmarshal(right, &rightMap)
  50. if err != nil {
  51. return nil, err
  52. }
  53. return differ.CompareObjects(leftMap, rightMap), nil
  54. }
  55. // CompareObjects compares two JSON object as map[string]interface{}
  56. // and return a Diff object.
  57. func (differ *Differ) CompareObjects(
  58. left map[string]interface{},
  59. right map[string]interface{},
  60. ) Diff {
  61. deltas := differ.compareMaps(left, right)
  62. return &diff{deltas: deltas}
  63. }
  64. // CompareArrays compares two JSON arrays as []interface{}
  65. // and return a Diff object.
  66. func (differ *Differ) CompareArrays(
  67. left []interface{},
  68. right []interface{},
  69. ) Diff {
  70. deltas := differ.compareArrays(left, right)
  71. return &diff{deltas: deltas}
  72. }
  73. func (differ *Differ) compareMaps(
  74. left map[string]interface{},
  75. right map[string]interface{},
  76. ) (deltas []Delta) {
  77. deltas = make([]Delta, 0)
  78. names := sortedKeys(left) // stabilize delta order
  79. for _, name := range names {
  80. if rightValue, ok := right[name]; ok {
  81. same, delta := differ.compareValues(Name(name), left[name], rightValue)
  82. if !same {
  83. deltas = append(deltas, delta)
  84. }
  85. } else {
  86. deltas = append(deltas, NewDeleted(Name(name), left[name]))
  87. }
  88. }
  89. names = sortedKeys(right) // stabilize delta order
  90. for _, name := range names {
  91. if _, ok := left[name]; !ok {
  92. deltas = append(deltas, NewAdded(Name(name), right[name]))
  93. }
  94. }
  95. return deltas
  96. }
  97. // ApplyPatch applies a Diff to an JSON object. This method is destructive.
  98. func (differ *Differ) ApplyPatch(json map[string]interface{}, patch Diff) {
  99. applyDeltas(patch.Deltas(), json)
  100. }
  101. type maybe struct {
  102. index int
  103. lcsIndex int
  104. item interface{}
  105. }
  106. func (differ *Differ) compareArrays(
  107. left []interface{},
  108. right []interface{},
  109. ) (deltas []Delta) {
  110. deltas = make([]Delta, 0)
  111. // LCS index pairs
  112. lcsPairs := lcs.New(left, right).IndexPairs()
  113. // list up items not in LCS, they are maybe deleted
  114. maybeDeleted := list.New() // but maybe moved or modified
  115. lcsI := 0
  116. for i, leftValue := range left {
  117. if lcsI < len(lcsPairs) && lcsPairs[lcsI].Left == i {
  118. lcsI++
  119. } else {
  120. maybeDeleted.PushBack(maybe{index: i, lcsIndex: lcsI, item: leftValue})
  121. }
  122. }
  123. // list up items not in LCS, they are maybe Added
  124. maybeAdded := list.New() // but maybe moved or modified
  125. lcsI = 0
  126. for i, rightValue := range right {
  127. if lcsI < len(lcsPairs) && lcsPairs[lcsI].Right == i {
  128. lcsI++
  129. } else {
  130. maybeAdded.PushBack(maybe{index: i, lcsIndex: lcsI, item: rightValue})
  131. }
  132. }
  133. // find moved items
  134. var delNext *list.Element // for prefetch to remove item in iteration
  135. for delCandidate := maybeDeleted.Front(); delCandidate != nil; delCandidate = delNext {
  136. delCan := delCandidate.Value.(maybe)
  137. delNext = delCandidate.Next()
  138. for addCandidate := maybeAdded.Front(); addCandidate != nil; addCandidate = addCandidate.Next() {
  139. addCan := addCandidate.Value.(maybe)
  140. if reflect.DeepEqual(delCan.item, addCan.item) {
  141. deltas = append(deltas, NewMoved(Index(delCan.index), Index(addCan.index), delCan.item, nil))
  142. maybeAdded.Remove(addCandidate)
  143. maybeDeleted.Remove(delCandidate)
  144. break
  145. }
  146. }
  147. }
  148. // find modified or add+del
  149. prevIndexDel := 0
  150. prevIndexAdd := 0
  151. delElement := maybeDeleted.Front()
  152. addElement := maybeAdded.Front()
  153. for i := 0; i <= len(lcsPairs); i++ { // not "< len(lcsPairs)"
  154. var lcsPair lcs.IndexPair
  155. var delSize, addSize int
  156. if i < len(lcsPairs) {
  157. lcsPair = lcsPairs[i]
  158. delSize = lcsPair.Left - prevIndexDel - 1
  159. addSize = lcsPair.Right - prevIndexAdd - 1
  160. prevIndexDel = lcsPair.Left
  161. prevIndexAdd = lcsPair.Right
  162. }
  163. var delSlice []maybe
  164. if delSize > 0 {
  165. delSlice = make([]maybe, 0, delSize)
  166. } else {
  167. delSlice = make([]maybe, 0, maybeDeleted.Len())
  168. }
  169. for ; delElement != nil; delElement = delElement.Next() {
  170. d := delElement.Value.(maybe)
  171. if d.lcsIndex != i {
  172. break
  173. }
  174. delSlice = append(delSlice, d)
  175. }
  176. var addSlice []maybe
  177. if addSize > 0 {
  178. addSlice = make([]maybe, 0, addSize)
  179. } else {
  180. addSlice = make([]maybe, 0, maybeAdded.Len())
  181. }
  182. for ; addElement != nil; addElement = addElement.Next() {
  183. a := addElement.Value.(maybe)
  184. if a.lcsIndex != i {
  185. break
  186. }
  187. addSlice = append(addSlice, a)
  188. }
  189. if len(delSlice) > 0 && len(addSlice) > 0 {
  190. var bestDeltas []Delta
  191. bestDeltas, delSlice, addSlice = differ.maximizeSimilarities(delSlice, addSlice)
  192. for _, delta := range bestDeltas {
  193. deltas = append(deltas, delta)
  194. }
  195. }
  196. for _, del := range delSlice {
  197. deltas = append(deltas, NewDeleted(Index(del.index), del.item))
  198. }
  199. for _, add := range addSlice {
  200. deltas = append(deltas, NewAdded(Index(add.index), add.item))
  201. }
  202. }
  203. return deltas
  204. }
  205. func (differ *Differ) compareValues(
  206. position Position,
  207. left interface{},
  208. right interface{},
  209. ) (same bool, delta Delta) {
  210. if reflect.TypeOf(left) != reflect.TypeOf(right) {
  211. return false, NewModified(position, left, right)
  212. }
  213. switch left.(type) {
  214. case map[string]interface{}:
  215. l := left.(map[string]interface{})
  216. childDeltas := differ.compareMaps(l, right.(map[string]interface{}))
  217. if len(childDeltas) > 0 {
  218. return false, NewObject(position, childDeltas)
  219. }
  220. case []interface{}:
  221. l := left.([]interface{})
  222. childDeltas := differ.compareArrays(l, right.([]interface{}))
  223. if len(childDeltas) > 0 {
  224. return false, NewArray(position, childDeltas)
  225. }
  226. default:
  227. if !reflect.DeepEqual(left, right) {
  228. if reflect.ValueOf(left).Kind() == reflect.String &&
  229. reflect.ValueOf(right).Kind() == reflect.String &&
  230. differ.textDiffMinimumLength <= len(left.(string)) {
  231. textDiff := dmp.New()
  232. patchs := textDiff.PatchMake(left.(string), right.(string))
  233. return false, NewTextDiff(position, patchs, left, right)
  234. } else {
  235. return false, NewModified(position, left, right)
  236. }
  237. }
  238. }
  239. return true, nil
  240. }
  241. func applyDeltas(deltas []Delta, object interface{}) interface{} {
  242. preDeltas := make(preDeltas, 0)
  243. for _, delta := range deltas {
  244. switch delta.(type) {
  245. case PreDelta:
  246. preDeltas = append(preDeltas, delta.(PreDelta))
  247. }
  248. }
  249. sort.Sort(preDeltas)
  250. for _, delta := range preDeltas {
  251. object = delta.PreApply(object)
  252. }
  253. postDeltas := make(postDeltas, 0, len(deltas)-len(preDeltas))
  254. for _, delta := range deltas {
  255. switch delta.(type) {
  256. case PostDelta:
  257. postDeltas = append(postDeltas, delta.(PostDelta))
  258. }
  259. }
  260. sort.Sort(postDeltas)
  261. for _, delta := range postDeltas {
  262. object = delta.PostApply(object)
  263. }
  264. return object
  265. }
  266. func (differ *Differ) maximizeSimilarities(left []maybe, right []maybe) (resultDeltas []Delta, freeLeft, freeRight []maybe) {
  267. deltaTable := make([][]Delta, len(left))
  268. for i := 0; i < len(left); i++ {
  269. deltaTable[i] = make([]Delta, len(right))
  270. }
  271. for i, leftValue := range left {
  272. for j, rightValue := range right {
  273. _, delta := differ.compareValues(Index(rightValue.index), leftValue.item, rightValue.item)
  274. deltaTable[i][j] = delta
  275. }
  276. }
  277. sizeX := len(left) + 1 // margins for both sides
  278. sizeY := len(right) + 1
  279. // fill out with similarities
  280. dpTable := make([][]float64, sizeX)
  281. for i := 0; i < sizeX; i++ {
  282. dpTable[i] = make([]float64, sizeY)
  283. }
  284. for x := sizeX - 2; x >= 0; x-- {
  285. for y := sizeY - 2; y >= 0; y-- {
  286. prevX := dpTable[x+1][y]
  287. prevY := dpTable[x][y+1]
  288. score := deltaTable[x][y].Similarity() + dpTable[x+1][y+1]
  289. dpTable[x][y] = max(prevX, prevY, score)
  290. }
  291. }
  292. minLength := len(left)
  293. if minLength > len(right) {
  294. minLength = len(right)
  295. }
  296. maxInvalidLength := minLength - 1
  297. freeLeft = make([]maybe, 0, len(left)-minLength)
  298. freeRight = make([]maybe, 0, len(right)-minLength)
  299. resultDeltas = make([]Delta, 0, minLength)
  300. var x, y int
  301. for x, y = 0, 0; x <= sizeX-2 && y <= sizeY-2; {
  302. current := dpTable[x][y]
  303. nextX := dpTable[x+1][y]
  304. nextY := dpTable[x][y+1]
  305. xValidLength := len(left) - maxInvalidLength + y
  306. yValidLength := len(right) - maxInvalidLength + x
  307. if x+1 < xValidLength && current == nextX {
  308. freeLeft = append(freeLeft, left[x])
  309. x++
  310. } else if y+1 < yValidLength && current == nextY {
  311. freeRight = append(freeRight, right[y])
  312. y++
  313. } else {
  314. resultDeltas = append(resultDeltas, deltaTable[x][y])
  315. x++
  316. y++
  317. }
  318. }
  319. for ; x < sizeX-1; x++ {
  320. freeLeft = append(freeLeft, left[x-1])
  321. }
  322. for ; y < sizeY-1; y++ {
  323. freeRight = append(freeRight, right[y-1])
  324. }
  325. return resultDeltas, freeLeft, freeRight
  326. }
  327. func deltasSimilarity(deltas []Delta) (similarity float64) {
  328. for _, delta := range deltas {
  329. similarity += delta.Similarity()
  330. }
  331. similarity = similarity / float64(len(deltas))
  332. return
  333. }
  334. func stringSimilarity(left, right string) (similarity float64) {
  335. matchingLength := float64(
  336. lcs.New(
  337. stringToInterfaceSlice(left),
  338. stringToInterfaceSlice(right),
  339. ).Length(),
  340. )
  341. similarity =
  342. (matchingLength / float64(len(left))) * (matchingLength / float64(len(right)))
  343. return
  344. }
  345. func stringToInterfaceSlice(str string) []interface{} {
  346. s := make([]interface{}, len(str))
  347. for i, v := range str {
  348. s[i] = v
  349. }
  350. return s
  351. }
  352. func sortedKeys(m map[string]interface{}) (keys []string) {
  353. keys = make([]string, 0, len(m))
  354. for key, _ := range m {
  355. keys = append(keys, key)
  356. }
  357. sort.Strings(keys)
  358. return
  359. }
  360. func max(first float64, rest ...float64) (max float64) {
  361. max = first
  362. for _, value := range rest {
  363. if max < value {
  364. max = value
  365. }
  366. }
  367. return max
  368. }