Skip to content

Repository files navigation

yojsonpatch

This library provides a way to apply JSON patches to Yojson values following the RFC 6902 specification.

Documentation

Modules

The library consists of three main modules:

Jsonpatch - Creating and applying JSON patches

The core module for working with JSON patches. A patch is a list of operations that can transform one JSON document into another.

Types:

  • operation - Represents a single JSON patch operation (Add, Remove, Replace, Move, Copy, Test)
  • t - A JSON patch (list of operations)

Key functions:

(* Parse a JSON patch from string *)valfrom_string : string -> t(* Apply a patch to a JSON document *)valapply : Yojson.Safe.t -> t -> Yojson.Safe.t(* Convert a patch to JSON *)valto_json : t -> Yojson.Safe.t

Convenience functions for creating operations:

valadd : string -> string -> operation(* Add value at path *)valremove : string -> operation(* Remove value at path *)valreplace : string -> string -> operation(* Replace value at path *)valcopy : string -> string -> operation(* Copy from source to destination *)valmove : string -> string -> operation(* Move from source to destination *)valtest : string -> string -> operation(* Test if value at path matches *)

Jsondiff - Generating patches by comparing documents

Generate JSON patches by comparing two JSON documents.

(* Generate a patch that transforms document 'a' into document 'b' *)valdiff : Yojson.Safe.t -> Yojson.Safe.t -> Jsonpatch.t

Jsonpointer - JSON Pointer utilities

Handles JSON Pointer paths (RFC 6901) used to identify locations within JSON documents.

Types:

  • part - Components of a JSON pointer (ObjectKey, ArrayIndex, etc.)
  • t - A JSON pointer (list of parts)

Key functions:

valfrom_string : string -> t(* Parse JSON pointer from string *)valto_string : t -> string(* Convert JSON pointer to string *)valequal : t -> t -> bool(* Compare two JSON pointers *)

Basic Usage

  1. Applying a patch:
let doc =Yojson.Safe.from_string {|{"name": "John"}|} inlet patch =Jsonpatch.[add "/age""30"] inlet result =Jsonpatch.apply doc patch
  1. Generating a patch:
let original =Yojson.Safe.from_string {|{"name": "John"}|} inlet modified =Yojson.Safe.from_string {|{"name": "Jane", "age": 30}|} inlet patch =Jsondiff.diff original modified
  1. Working with JSON pointers:
let ptr =Jsonpointer.from_string "/users/0/name"inlet path_str =Jsonpointer.to_string ptr

Examples

openYojsonpatchopenJsonpatch(** Define a json document, it must be converted to a Yojson.Safe.t *)let doc_str =
{|
{
"title": "UNIX: A History and a Memoir"
}
|}
;;
(** We can create a patch from its JSON form as a string using the [Jsonpatch.from_string str] function or using convenience functions like add below *)let patch =
[ add "/author" {|"Brian W. Kernighan"|}
; add "/published""2020"
; add "/publisher" {|"Kindle Direct Publishing"|}
; add "/stars""5"
; add "/purchased" {|"2019-10-22"|}
]
;;
let()=let doc =Yojson.Safe.from_string doc_str inlet patched_doc =Jsonpatch.apply doc patch inlet gen_patch =Jsondiff.diff doc patched_doc inFormat.printf
"Original doc:\n%s\nPatch:\n%s\nPatched doc:\n%s\nGenerated patch:\n%s\n"
(Yojson.Safe.pretty_to_string doc)
(Yojson.Safe.pretty_to_string (to_json patch))
(Yojson.Safe.pretty_to_string patched_doc)
(Yojson.Safe.pretty_to_string (to_json gen_patch))
;;

To run the example:

Original doc:
{ "title": "UNIX: A History and a Memoir" }
Patch:
[
{ "op": "add", "path": "author", "value": "Brian W. Kernighan" },
{ "op": "add", "path": "published", "value": 2020 },
{ "op": "add", "path": "publisher", "value": "Kindle Direct Publishing" },
{ "op": "add", "path": "stars", "value": 5 },
{ "op": "add", "path": "purchased", "value": "2019-10-22" }
]
Patched doc:
{
"title": "UNIX: A History and a Memoir",
"author": "Brian W. Kernighan",
"published": 2020,
"publisher": "Kindle Direct Publishing",
"stars": 5,
"purchased": "2019-10-22"
}
Generated patch:
[
{ "op": "add", "path": "purchased", "value": "2019-10-22" },
{ "op": "add", "path": "stars", "value": 5 },
{ "op": "add", "path": "publisher", "value": "Kindle Direct Publishing" },
{ "op": "add", "path": "published", "value": 2020 },
{ "op": "add", "path": "author", "value": "Brian W. Kernighan" }
]

Benchmarks

Using the program in example/jp, we've applied a 45MB patch to a 17MB document generating a 26MB of prettified patched document in around 14 secs, using less than 1GB of memory in a Apple M1 CPU.

Don't know if this is good or bad yet.

TODO

  • Create .mli files to export the required API
  • Add more tests
  • fix skipped tests
  • May be remove Core depdendency
  • Implement JSON patches generation
  • Publish it to opam
  • Benchmarks

Related projects

License

Apache 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt

About

A JSON Patch library for OCAML

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages