{"id":13801511,"url":"https://github.com/gnieh/diffson","last_synced_at":"2025-04-07T08:14:41.543Z","repository":{"id":10352397,"uuid":"12490016","full_name":"gnieh/diffson","owner":"gnieh","description":"A scala diff/patch library for Json","archived":false,"fork":false,"pushed_at":"2024-09-21T14:06:56.000Z","size":794,"stargazers_count":317,"open_issues_count":13,"forks_count":51,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-03-31T07:04:17.851Z","etag":null,"topics":["diff","json","json-patch","patch","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gnieh.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"satabin","ko_fi":"satabin","liberapay":"gnieh"}},"created_at":"2013-08-30T15:50:35.000Z","updated_at":"2025-03-19T21:43:59.000Z","dependencies_parsed_at":"2024-03-25T18:14:57.596Z","dependency_job_id":"0790163f-a727-4df3-9639-b097e7fb082d","html_url":"https://github.com/gnieh/diffson","commit_stats":{"total_commits":309,"total_committers":33,"mean_commits":9.363636363636363,"dds":0.4563106796116505,"last_synced_commit":"2ecbd655f3cad293bb7454a1fd0cc3078b017561"},"previous_names":[],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnieh%2Fdiffson","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnieh%2Fdiffson/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnieh%2Fdiffson/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnieh%2Fdiffson/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gnieh","download_url":"https://codeload.github.com/gnieh/diffson/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247615377,"owners_count":20967184,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["diff","json","json-patch","patch","scala"],"created_at":"2024-08-04T00:01:23.706Z","updated_at":"2025-04-07T08:14:41.519Z","avatar_url":"https://github.com/gnieh.png","language":"Scala","readme":"Gnieh Diffson\n[![Build Status](https://travis-ci.org/gnieh/diffson.png)](https://travis-ci.org/gnieh/diffson) \n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/9892e2c968974ecb951d21969adbadaa)](https://www.codacy.com/app/satabin/diffson?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=gnieh/diffson\u0026amp;utm_campaign=Badge_Grade) \n[![Code Coverage](https://codecov.io/github/gnieh/diffson/coverage.svg?branch=master)](https://codecov.io/github/gnieh/diffson?branch=master) \n[![Maven Central](https://img.shields.io/maven-central/v/org.gnieh/diffson-core_2.13.svg)](https://maven-badges.herokuapp.com/maven-central/org.gnieh/diffson-core_2.13) \n[![Scaladoc](https://javadoc.io/badge/org.gnieh/diffson-core_2.13.svg)](https://javadoc.io/doc/org.gnieh/diffson-core_2.13)\n\n----\n\n[![Join the chat at https://gitter.im/gnieh/diffson](https://badges.gitter.im/gnieh/diffson.svg)](https://gitter.im/gnieh/diffson?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\nA [Scala][6] implementation of the [RFC-6901][1], [RFC-6902][2], and [RFC-7396][11].\nIt also provides methods to compute _diffs_ between two Json values that produce valid Json patches or merge patches.\n\n**Note:** if you still want to use the `3.x.y` series (without [cats][cats]), please see [this documentation][diffson3]\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n## Table of Contents\n\n- [Getting Started](#getting-started)\n- [Json Library](#json-library)\n  - [Note on (de)serialization](#note-on-deserialization)\n- [Json Patch (RFC-6902)](#json-patch-rfc-6902)\n  - [Basic Usage](#basic-usage)\n  - [Simple diffs](#simple-diffs)\n  - [Remembering old values](#remembering-old-values)\n- [Json Merge Patches (RFC-7396)](#json-merge-patches-rfc-7396)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\nGetting Started\n---------------\n\nThis library is published in the [Maven][7] [Central Repository][8].\nYou can add it to your sbt project by putting this line into your build description:\n```scala\nlibraryDependencies += \"org.gnieh\" %% f\"diffson-$jsonLib\" % \"4.1.1\"\n```\n\nwhere `jsonLib` is either:\n\n - `spray-json`\n - `play-json`\n - `circe`\n - `ujson` for ujson/upickle\n\nThese versions are built for Scala 2.12, 2.13, and 3.\n\nScala.JS is also supported for Scala 2.12, 2.13, and 3. \nTo use it, add this dependency to your build file:\n```scala\nlibraryDependencies += \"org.gnieh\" %%% f\"diffson-$jsonLib\" % \"4.1.1\"\n```\n\nJson Library\n------------\n\nDiffson was first developed for [spray-json][3], however, it is possible to use it with any json library of your liking.\nThe only requirement is to have a `Jsony` for your json library.\n`Jsony` is a type class describing what operations are required to compute diffs and apply patches to Json-like types.\n\nAt the moment, diffson provides instances for [spray-json][3], [Play! Json][9], and [circe][10].\nTo use these implementations you need to link with the correct module and import the instance:\n\n```scala\n// spray-json\nimport diffson.sprayJson._\n// play-json\nimport diffson.playJson._\n// circe\nimport diffson.circe._\n```\n\nIf you want to add support for your favorite Json library, you may only depend on diffson core module `diffson-core` and all you need to do then is to implement the `Jsony` class, which provides all the operations for diffson to be able to compute diffs and apply patches. \nContribution of new Json libraries in this repository are more than welcome.\n\n### Note on (de)serialization\n\nThe purpose of diffson is to create and manipulate diffs and patch for Json like structures. \nHowever the supported patch formats can also be represented as Json objects. \nThe core library doesn't mention any of this, as its sole purpose is the diff/patch computations. \nGiven the variety of Json libraries out there and there various ways of implementing the way of (de)serializing Json values, there is no good abstraction that fits this general purpose library, and this is up to the library user to do it in the most appropriate approach given the Json library of their choosing. \nThe various supported Json libraries in diffson provide an idiomatic way of (de)serializing the different element for each of them (e.g. the `circe` module provide `Decoder`s and `Encoder`s for all the patch types).\n\nFor instance to get circe encoder and decoder instances, you need to\n```scala\nimport io.circe._\nimport diffson.circe._\nimport diffson.jsonpatch._\n\nval decoder = Decoder[JsonPatch[Json]]\nval encoder = Encoder[JsonPatch[Json]]\n```\n\nFor Play! Json, you need to\n```scala\nimport play.api.libs.json._\nimport diffson.playJson._\nimport diffson.playJson.DiffsonProtocol._\nimport diffson.jsonpatch._\n\nval format = Json.format[JsonPatch[JsValue]]\n```\n\nFor Spray Json, you need to\n```scala\nimport spray.json._\nimport diffson.sprayJson._\nimport diffson.sprayJson.DiffsonProtocol._\nimport diffson.jsonpatch._\n\nval format = implicitly[JsonFormat[JsonPatch[JsValue]]]\n```\n\nJson Patch (RFC-6902)\n---------------------\n\n### Basic Usage\n\nAlthough the library is quite small and easy to use, here comes a summary of its basic usage.\nDiffson uses a type-class approach based on the [cats][cats] library.\nAll operations that may fail are wrapped in type with a `MonadError` instance.\n\nThere are two different entities living in the `diffson.jsonpatch` and one on `diffson.jsonpointer` package useful to work with Json patches:\n - `Pointer` which allows to parse and manipulate Json pointers as defined in [RFC-6901][1],\n - `JsonPatch` which allows to parse, create and apply Json patches as defined in [RFC-6902][2],\n - `JsonDiff` which allows to compute the diff between two Json values and create Json patches.\n\nBasically if someone wants to compute the diff between two Json objects, they can execute the following:\n```scala\nimport diffson._\nimport diffson.lcs._\nimport diffson.circe._\nimport diffson.jsonpatch._\nimport diffson.jsonpatch.lcsdiff._\n\nimport io.circe._\nimport io.circe.parser._\n\nimport cats._\nimport cats.implicits._\n\nimplicit val lcs = new Patience[Json]\n\nval json1 = parse(\"\"\"{\n                    |  \"a\": 1,\n                    |  \"b\": true,\n                    |  \"c\": [\"test\", \"plop\"]\n                    |}\"\"\".stripMargin)\n\nval json2 = parse(\"\"\"{\n                    |  \"a\": 6,\n                    |  \"c\": [\"test2\", \"plop\"],\n                    |  \"d\": false\n                    |}\"\"\".stripMargin)\n\nval patch =\n  for {\n    json1 \u003c- json1\n    json2 \u003c- json2\n  } yield diff(json1, json2)\n```\nwhich will return a patch that can be serialized in json as:\n```json\n[{\n  \"op\":\"replace\",\n  \"path\":\"/a\",\n  \"value\":6\n},{\n  \"op\":\"remove\",\n  \"path\":\"/b\"\n},{\n  \"op\":\"replace\",\n  \"path\":\"/c/0\",\n  \"value\":\"test2\"\n},{\n  \"op\":\"add\",\n  \"path\":\"/d\",\n  \"value\":false\n}]\n```\n\nThis example computes a diff based on an LCS, so we must provide an implicit instance of `Lcs`.\nIn that case we used the `Patience` instance, but other could be used.\nSee package `diffson.lcs` to see what implementations are available by default, or provide your own.\n\nYou can then apply an existing patch to a Json object as follows:\n```scala\nimport scala.util.Try\n\nimport cats.implicits._\n\nval json2 = patch[Try](json1)\n```\n\nwhich results in a json like:\n```json\n{\n  \"d\":false,\n  \"c\":\"test2\",\n  \"a\":6\n}\n```\nwhich we can easily verify is the same as `json2` modulo reordering of fields.\n\nA patch may fail, this is why the `apply` method wraps the result in an `F[_]` with a `MonadError`.\nIn this example, we used the standard `Try` class, but any type `F` with the appropriate `MonadError[F, Throwable]` instance in scope can be used.\n\n### Simple diffs\n\nThe example above uses an LCS based diff, which makes it possible to have smart diffs for arrays.\nHowever, depending on your use case, this feature might not be what you want:\n - LCS can be intensive to compute if you have huge arrays;\n - you might want to see a modified array as a single `replace` operation.\n\nTo do so, instead of importing `diffson.jsonpatch.lcsdiff._`, import `diffson.jsonpatch.simplediff._` and you do not need to provide an `Lcs` instance.\nResulting diff will be bigger in case of different arrays, but quicker to compute.\n\nFor instance, the resulting simple diff for the example above is:\n```json\n[\n  {\n    \"op\" : \"replace\",\n    \"path\" : \"/a\",\n    \"value\" : 6\n  },\n  {\n    \"op\" : \"remove\",\n    \"path\" : \"/b\"\n  },\n  {\n    \"op\" : \"replace\",\n    \"path\" : \"/c\",\n    \"value\" : [\n      \"test2\",\n      \"plop\"\n    ]\n  },\n  {\n    \"op\" : \"add\",\n    \"path\" : \"/d\",\n    \"value\" : false\n  }\n]\n```\n\nNote the `replace` operation for the entire array, instead of the single modified element.\n\n### Remembering old values\n\nWhether you use the LCS based or simple diff, you can make it remember old values for `remove` and `replace` operations.\n\nTo that end, you just need to import `diffson.jsonpatch.lcsdiff.remembering._` or `diffson.jsonpatch.simplediff.remembering._` instead.\nThe generated diff will add an `old` field to `remove` and `replace` operations in the patch, containing the previous version of the field in original object.\nTaking the first example with the new import, we have similar code.\n\n```scala\nimport diffson._\nimport diffson.lcs._\nimport diffson.circe._\nimport diffson.jsonpatch._\nimport diffson.jsonpatch.lcsdiff.remembering._\n\nimport io.circe._\nimport io.circe.parser._\n\nimport cats._\nimport cats.implicits._\n\nimplicit val lcs = new Patience[Json]\n\nval json1 = parse(\"\"\"{\n                    |  \"a\": 1,\n                    |  \"b\": true,\n                    |  \"c\": [\"test\", \"plop\"]\n                    |}\"\"\".stripMargin)\n\nval json2 = parse(\"\"\"{\n                    |  \"a\": 6,\n                    |  \"c\": [\"test2\", \"plop\"],\n                    |  \"d\": false\n                    |}\"\"\".stripMargin)\n\nval patch =\n  for {\n    json1 \u003c- json1\n    json2 \u003c- json2\n  } yield diff(json1, json2)\n```\n\nwhich results in a result with the old value remembered in the patch:\n\n```json\n[\n  {\n    \"op\" : \"replace\",\n    \"path\" : \"/a\",\n    \"value\" : 6,\n    \"old\" : 1\n  },\n  {\n    \"op\" : \"remove\",\n    \"path\" : \"/b\",\n    \"old\" : true\n  },\n  {\n    \"op\" : \"replace\",\n    \"path\" : \"/c/0\",\n    \"value\" : \"test2\",\n    \"old\" : \"test\"\n  },\n  {\n    \"op\" : \"add\",\n    \"path\" : \"/d\",\n    \"value\" : false\n  }\n]\n```\n\nPatches produced with this methods are still valid according to the RFC, as the new field must simply be ignored by implementations that are not aware of this encoding, so interoperability is not broken.\n\nJson Merge Patches (RFC-7396)\n-----------------------------\n\nThere are two different entities living in the `diffson.jsonmergepatch` package useful to work with Json merge patches:\n - `JsonMergePatch` which allows to parse, create and apply Json merge patches as defined in [RFC-7396][11],\n - `JsonMergeDiff` which allows to compute the diff between two Json values and create Json merge patches.\n\nBasically if someone wants to compute the diff between two Json objects, they can execute the following:\n```scala\nimport diffson._\nimport diffson.circe._\nimport diffson.jsonmergepatch._\n\nimport io.circe.parser._\nimport io.circe.syntax._\n\nval json1 = parse(\"\"\"{\n              |  \"a\": 1,\n              |  \"b\": true,\n              |  \"c\": \"test\"\n              |}\"\"\".stripMargin)\n\nval json2 = parse(\"\"\"{\n              |  \"a\": 6,\n              |  \"c\": \"test2\",\n              |  \"d\": false\n              |}\"\"\".stripMargin)\n\nval patch =\n  for {\n    json1 \u003c- json1\n    json2 \u003c- json2\n  } yield diff(json1, json2)\n```\nwhich will return the following Json Merge Patch:\n```json\n{\n  \"a\": 6,\n  \"b\": null,\n  \"c\": \"test2\",\n  \"d\": false\n}\n```\nYou can then apply the patch to `json1`:\n```scala\nval json3 = patch(json1)\n```\n\nwhich will create the following Json:\n```json\n{\n  \"d\":false,\n  \"c\":\"test2\",\n  \"a\":6\n}\n```\nwhich we can easily verify is the same as `json2` modulo reordering of fields.\n\n[1]: https://datatracker.ietf.org/doc/html/rfc6901\n[2]: https://datatracker.ietf.org/doc/html/rfc6902\n[3]: https://github.com/spray/spray-json\n[4]: http://alfedenzo.livejournal.com/170301.html\n[5]: https://en.wikipedia.org/wiki/Longest_common_subsequence_problem\n[6]: http://scala-lang.org\n[7]: http://maven.apache.org/\n[8]: http://search.maven.org/\n[9]: https://www.playframework.com/documentation/latest/ScalaJson\n[10]: https://circe.github.io/circe/\n[11]: https://datatracker.ietf.org/doc/html/rfc7396\n[cats]: https://typelevel.org/cats/\n[Foldable]: https://typelevel.org/cats/typeclasses/foldable.html\n[diffson3]: https://github.com/gnieh/diffson/tree/v3.1.x\n","funding_links":["https://github.com/sponsors/satabin","https://ko-fi.com/satabin","https://liberapay.com/gnieh"],"categories":["Table of Contents","JSON"],"sub_categories":["JSON"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgnieh%2Fdiffson","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgnieh%2Fdiffson","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgnieh%2Fdiffson/lists"}