deltas.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. package gojsondiff
  2. import (
  3. "errors"
  4. dmp "github.com/sergi/go-diff/diffmatchpatch"
  5. "reflect"
  6. "strconv"
  7. )
  8. // A Delta represents an atomic difference between two JSON objects.
  9. type Delta interface {
  10. // Similarity calculates the similarity of the Delta values.
  11. // The return value is normalized from 0 to 1,
  12. // 0 is completely different and 1 is they are same
  13. Similarity() (similarity float64)
  14. }
  15. // To cache the calculated similarity,
  16. // concrete Deltas can use similariter and similarityCache
  17. type similariter interface {
  18. similarity() (similarity float64)
  19. }
  20. type similarityCache struct {
  21. similariter
  22. value float64
  23. }
  24. func newSimilarityCache(sim similariter) similarityCache {
  25. cache := similarityCache{similariter: sim, value: -1}
  26. return cache
  27. }
  28. func (cache similarityCache) Similarity() (similarity float64) {
  29. if cache.value < 0 {
  30. cache.value = cache.similariter.similarity()
  31. }
  32. return cache.value
  33. }
  34. // A Position represents the position of a Delta in an object or an array.
  35. type Position interface {
  36. // String returns the position as a string
  37. String() (name string)
  38. // CompareTo returns a true if the Position is smaller than another Position.
  39. // This function is used to sort Positions by the sort package.
  40. CompareTo(another Position) bool
  41. }
  42. // A Name is a Postition with a string, which means the delta is in an object.
  43. type Name string
  44. func (n Name) String() (name string) {
  45. return string(n)
  46. }
  47. func (n Name) CompareTo(another Position) bool {
  48. return n < another.(Name)
  49. }
  50. // A Index is a Position with an int value, which means the Delta is in an Array.
  51. type Index int
  52. func (i Index) String() (name string) {
  53. return strconv.Itoa(int(i))
  54. }
  55. func (i Index) CompareTo(another Position) bool {
  56. return i < another.(Index)
  57. }
  58. // A PreDelta is a Delta that has a position of the left side JSON object.
  59. // Deltas implements this interface should be applies before PostDeltas.
  60. type PreDelta interface {
  61. // PrePosition returns the Position.
  62. PrePosition() Position
  63. // PreApply applies the delta to object.
  64. PreApply(object interface{}) interface{}
  65. }
  66. type preDelta struct{ Position }
  67. func (i preDelta) PrePosition() Position {
  68. return Position(i.Position)
  69. }
  70. type preDeltas []PreDelta
  71. // for sorting
  72. func (s preDeltas) Len() int {
  73. return len(s)
  74. }
  75. // for sorting
  76. func (s preDeltas) Swap(i, j int) {
  77. s[i], s[j] = s[j], s[i]
  78. }
  79. // for sorting
  80. func (s preDeltas) Less(i, j int) bool {
  81. return !s[i].PrePosition().CompareTo(s[j].PrePosition())
  82. }
  83. // A PreDelta is a Delta that has a position of the right side JSON object.
  84. // Deltas implements this interface should be applies after PreDeltas.
  85. type PostDelta interface {
  86. // PostPosition returns the Position.
  87. PostPosition() Position
  88. // PostApply applies the delta to object.
  89. PostApply(object interface{}) interface{}
  90. }
  91. type postDelta struct{ Position }
  92. func (i postDelta) PostPosition() Position {
  93. return Position(i.Position)
  94. }
  95. type postDeltas []PostDelta
  96. // for sorting
  97. func (s postDeltas) Len() int {
  98. return len(s)
  99. }
  100. // for sorting
  101. func (s postDeltas) Swap(i, j int) {
  102. s[i], s[j] = s[j], s[i]
  103. }
  104. // for sorting
  105. func (s postDeltas) Less(i, j int) bool {
  106. return s[i].PostPosition().CompareTo(s[j].PostPosition())
  107. }
  108. // An Object is a Delta that represents an object of JSON
  109. type Object struct {
  110. postDelta
  111. similarityCache
  112. // Deltas holds internal Deltas
  113. Deltas []Delta
  114. }
  115. // NewObject returns an Object
  116. func NewObject(position Position, deltas []Delta) *Object {
  117. d := Object{postDelta: postDelta{position}, Deltas: deltas}
  118. d.similarityCache = newSimilarityCache(&d)
  119. return &d
  120. }
  121. func (d *Object) PostApply(object interface{}) interface{} {
  122. switch object.(type) {
  123. case map[string]interface{}:
  124. o := object.(map[string]interface{})
  125. n := string(d.PostPosition().(Name))
  126. o[n] = applyDeltas(d.Deltas, o[n])
  127. case []interface{}:
  128. o := object.([]interface{})
  129. n := int(d.PostPosition().(Index))
  130. o[n] = applyDeltas(d.Deltas, o[n])
  131. }
  132. return object
  133. }
  134. func (d *Object) similarity() (similarity float64) {
  135. similarity = deltasSimilarity(d.Deltas)
  136. return
  137. }
  138. // An Array is a Delta that represents an array of JSON
  139. type Array struct {
  140. postDelta
  141. similarityCache
  142. // Deltas holds internal Deltas
  143. Deltas []Delta
  144. }
  145. // NewArray returns an Array
  146. func NewArray(position Position, deltas []Delta) *Array {
  147. d := Array{postDelta: postDelta{position}, Deltas: deltas}
  148. d.similarityCache = newSimilarityCache(&d)
  149. return &d
  150. }
  151. func (d *Array) PostApply(object interface{}) interface{} {
  152. switch object.(type) {
  153. case map[string]interface{}:
  154. o := object.(map[string]interface{})
  155. n := string(d.PostPosition().(Name))
  156. o[n] = applyDeltas(d.Deltas, o[n])
  157. case []interface{}:
  158. o := object.([]interface{})
  159. n := int(d.PostPosition().(Index))
  160. o[n] = applyDeltas(d.Deltas, o[n])
  161. }
  162. return object
  163. }
  164. func (d *Array) similarity() (similarity float64) {
  165. similarity = deltasSimilarity(d.Deltas)
  166. return
  167. }
  168. // An Added represents a new added field of an object or an array
  169. type Added struct {
  170. postDelta
  171. similarityCache
  172. // Values holds the added value
  173. Value interface{}
  174. }
  175. // NewAdded returns a new Added
  176. func NewAdded(position Position, value interface{}) *Added {
  177. d := Added{postDelta: postDelta{position}, Value: value}
  178. return &d
  179. }
  180. func (d *Added) PostApply(object interface{}) interface{} {
  181. switch object.(type) {
  182. case map[string]interface{}:
  183. object.(map[string]interface{})[string(d.PostPosition().(Name))] = d.Value
  184. case []interface{}:
  185. i := int(d.PostPosition().(Index))
  186. o := object.([]interface{})
  187. if i < len(o) {
  188. o = append(o, 0) //dummy
  189. copy(o[i+1:], o[i:])
  190. o[i] = d.Value
  191. object = o
  192. } else {
  193. object = append(o, d.Value)
  194. }
  195. }
  196. return object
  197. }
  198. func (d *Added) similarity() (similarity float64) {
  199. return 0
  200. }
  201. // A Modified represents a field whose value is changed.
  202. type Modified struct {
  203. postDelta
  204. similarityCache
  205. // The value before modification
  206. OldValue interface{}
  207. // The value after modification
  208. NewValue interface{}
  209. }
  210. // NewModified returns a Modified
  211. func NewModified(position Position, oldValue, newValue interface{}) *Modified {
  212. d := Modified{
  213. postDelta: postDelta{position},
  214. OldValue: oldValue,
  215. NewValue: newValue,
  216. }
  217. d.similarityCache = newSimilarityCache(&d)
  218. return &d
  219. }
  220. func (d *Modified) PostApply(object interface{}) interface{} {
  221. switch object.(type) {
  222. case map[string]interface{}:
  223. // TODO check old value
  224. object.(map[string]interface{})[string(d.PostPosition().(Name))] = d.NewValue
  225. case []interface{}:
  226. object.([]interface{})[int(d.PostPosition().(Index))] = d.NewValue
  227. }
  228. return object
  229. }
  230. func (d *Modified) similarity() (similarity float64) {
  231. similarity += 0.3 // at least, they are at the same position
  232. if reflect.TypeOf(d.OldValue) == reflect.TypeOf(d.NewValue) {
  233. similarity += 0.3 // types are same
  234. switch d.OldValue.(type) {
  235. case string:
  236. similarity += 0.4 * stringSimilarity(d.OldValue.(string), d.NewValue.(string))
  237. case float64:
  238. ratio := d.OldValue.(float64) / d.NewValue.(float64)
  239. if ratio > 1 {
  240. ratio = 1 / ratio
  241. }
  242. similarity += 0.4 * ratio
  243. }
  244. }
  245. return
  246. }
  247. // A TextDiff represents a Modified with TextDiff between the old and the new values.
  248. type TextDiff struct {
  249. Modified
  250. // Diff string
  251. Diff []dmp.Patch
  252. }
  253. // NewTextDiff returns
  254. func NewTextDiff(position Position, diff []dmp.Patch, oldValue, newValue interface{}) *TextDiff {
  255. d := TextDiff{
  256. Modified: *NewModified(position, oldValue, newValue),
  257. Diff: diff,
  258. }
  259. return &d
  260. }
  261. func (d *TextDiff) PostApply(object interface{}) interface{} {
  262. switch object.(type) {
  263. case map[string]interface{}:
  264. o := object.(map[string]interface{})
  265. i := string(d.PostPosition().(Name))
  266. // TODO error
  267. d.OldValue = o[i]
  268. // TODO error
  269. d.patch()
  270. o[i] = d.NewValue
  271. case []interface{}:
  272. o := object.([]interface{})
  273. i := d.PostPosition().(Index)
  274. d.OldValue = o[i]
  275. // TODO error
  276. d.patch()
  277. o[i] = d.NewValue
  278. }
  279. return object
  280. }
  281. func (d *TextDiff) patch() error {
  282. if d.OldValue == nil {
  283. return errors.New("Old Value is not set")
  284. }
  285. patcher := dmp.New()
  286. patched, successes := patcher.PatchApply(d.Diff, d.OldValue.(string))
  287. for _, success := range successes {
  288. if !success {
  289. return errors.New("Failed to apply a patch")
  290. }
  291. }
  292. d.NewValue = patched
  293. return nil
  294. }
  295. func (d *TextDiff) DiffString() string {
  296. dmp := dmp.New()
  297. return dmp.PatchToText(d.Diff)
  298. }
  299. // A Delted represents deleted field or index of an Object or an Array.
  300. type Deleted struct {
  301. preDelta
  302. // The value deleted
  303. Value interface{}
  304. }
  305. // NewDeleted returns a Deleted
  306. func NewDeleted(position Position, value interface{}) *Deleted {
  307. d := Deleted{
  308. preDelta: preDelta{position},
  309. Value: value,
  310. }
  311. return &d
  312. }
  313. func (d *Deleted) PreApply(object interface{}) interface{} {
  314. switch object.(type) {
  315. case map[string]interface{}:
  316. // TODO check old value
  317. delete(object.(map[string]interface{}), string(d.PrePosition().(Name)))
  318. case []interface{}:
  319. i := int(d.PrePosition().(Index))
  320. o := object.([]interface{})
  321. object = append(o[:i], o[i+1:]...)
  322. }
  323. return object
  324. }
  325. func (d Deleted) Similarity() (similarity float64) {
  326. return 0
  327. }
  328. // A Moved represents field that is moved, which means the index or name is
  329. // changed. Note that, in this library, assigning a Moved and a Modified to
  330. // a single position is not allowed. For the compatibility with jsondiffpatch,
  331. // the Moved in this library can hold the old and new value in it.
  332. type Moved struct {
  333. preDelta
  334. postDelta
  335. similarityCache
  336. // The value before moving
  337. Value interface{}
  338. // The delta applied after moving (for compatibility)
  339. Delta interface{}
  340. }
  341. func NewMoved(oldPosition Position, newPosition Position, value interface{}, delta Delta) *Moved {
  342. d := Moved{
  343. preDelta: preDelta{oldPosition},
  344. postDelta: postDelta{newPosition},
  345. Value: value,
  346. Delta: delta,
  347. }
  348. d.similarityCache = newSimilarityCache(&d)
  349. return &d
  350. }
  351. func (d *Moved) PreApply(object interface{}) interface{} {
  352. switch object.(type) {
  353. case map[string]interface{}:
  354. //not supported
  355. case []interface{}:
  356. i := int(d.PrePosition().(Index))
  357. o := object.([]interface{})
  358. d.Value = o[i]
  359. object = append(o[:i], o[i+1:]...)
  360. }
  361. return object
  362. }
  363. func (d *Moved) PostApply(object interface{}) interface{} {
  364. switch object.(type) {
  365. case map[string]interface{}:
  366. //not supported
  367. case []interface{}:
  368. i := int(d.PostPosition().(Index))
  369. o := object.([]interface{})
  370. o = append(o, 0) //dummy
  371. copy(o[i+1:], o[i:])
  372. o[i] = d.Value
  373. object = o
  374. }
  375. if d.Delta != nil {
  376. d.Delta.(PostDelta).PostApply(object)
  377. }
  378. return object
  379. }
  380. func (d *Moved) similarity() (similarity float64) {
  381. similarity = 0.6 // as type and contens are same
  382. ratio := float64(d.PrePosition().(Index)) / float64(d.PostPosition().(Index))
  383. if ratio > 1 {
  384. ratio = 1 / ratio
  385. }
  386. similarity += 0.4 * ratio
  387. return
  388. }