json_patch/
lib.rs

1//! A [JSON Patch (RFC 6902)](https://tools.ietf.org/html/rfc6902) and
2//! [JSON Merge Patch (RFC 7396)](https://tools.ietf.org/html/rfc7396) implementation for Rust.
3//!
4//! # Usage
5//!
6//! Add this to your *Cargo.toml*:
7//! ```toml
8//! [dependencies]
9//! json-patch = "*"
10//! ```
11//!
12//! # Examples
13//! Create and patch document using JSON Patch:
14//!
15//! ```rust
16//! #[macro_use]
17//! use json_patch::{Patch, patch};
18//! use serde_json::{from_value, json};
19//!
20//! # pub fn main() {
21//! let mut doc = json!([
22//!     { "name": "Andrew" },
23//!     { "name": "Maxim" }
24//! ]);
25//!
26//! let p: Patch = from_value(json!([
27//!   { "op": "test", "path": "/0/name", "value": "Andrew" },
28//!   { "op": "add", "path": "/0/happy", "value": true }
29//! ])).unwrap();
30//!
31//! patch(&mut doc, &p).unwrap();
32//! assert_eq!(doc, json!([
33//!   { "name": "Andrew", "happy": true },
34//!   { "name": "Maxim" }
35//! ]));
36//!
37//! # }
38//! ```
39//!
40//! Create and patch document using JSON Merge Patch:
41//!
42//! ```rust
43//! #[macro_use]
44//! use json_patch::merge;
45//! use serde_json::json;
46//!
47//! # pub fn main() {
48//! let mut doc = json!({
49//!   "title": "Goodbye!",
50//!   "author" : {
51//!     "givenName" : "John",
52//!     "familyName" : "Doe"
53//!   },
54//!   "tags":[ "example", "sample" ],
55//!   "content": "This will be unchanged"
56//! });
57//!
58//! let patch = json!({
59//!   "title": "Hello!",
60//!   "phoneNumber": "+01-123-456-7890",
61//!   "author": {
62//!     "familyName": null
63//!   },
64//!   "tags": [ "example" ]
65//! });
66//!
67//! merge(&mut doc, &patch);
68//! assert_eq!(doc, json!({
69//!   "title": "Hello!",
70//!   "author" : {
71//!     "givenName" : "John"
72//!   },
73//!   "tags": [ "example" ],
74//!   "content": "This will be unchanged",
75//!   "phoneNumber": "+01-123-456-7890"
76//! }));
77//! # }
78//! ```
79#![warn(missing_docs)]
80
81use jsonptr::{index::Index, Pointer, PointerBuf};
82use serde::{Deserialize, Serialize};
83use serde_json::{Map, Value};
84use std::fmt::{self, Display, Formatter};
85use thiserror::Error;
86
87// So users can instance `jsonptr::PointerBuf` and others without
88// having to explicitly match our `jsonptr` version.
89pub use jsonptr;
90
91#[cfg(feature = "diff")]
92mod diff;
93
94#[cfg(feature = "diff")]
95pub use self::diff::diff;
96
97struct WriteAdapter<'a>(&'a mut dyn fmt::Write);
98
99impl std::io::Write for WriteAdapter<'_> {
100    fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
101        let s = std::str::from_utf8(buf).unwrap();
102        self.0
103            .write_str(s)
104            .map_err(|_| std::io::Error::from(std::io::ErrorKind::Other))?;
105        Ok(buf.len())
106    }
107
108    fn flush(&mut self) -> Result<(), std::io::Error> {
109        Ok(())
110    }
111}
112
113macro_rules! impl_display {
114    ($name:ident) => {
115        impl Display for $name {
116            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
117                let alternate = f.alternate();
118                if alternate {
119                    serde_json::to_writer_pretty(WriteAdapter(f), self)
120                        .map_err(|_| std::fmt::Error)?;
121                } else {
122                    serde_json::to_writer(WriteAdapter(f), self).map_err(|_| std::fmt::Error)?;
123                }
124                Ok(())
125            }
126        }
127    };
128}
129
130/// Representation of JSON Patch (list of patch operations)
131#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
132#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
133#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
134pub struct Patch(pub Vec<PatchOperation>);
135
136impl_display!(Patch);
137
138impl std::ops::Deref for Patch {
139    type Target = [PatchOperation];
140
141    fn deref(&self) -> &[PatchOperation] {
142        &self.0
143    }
144}
145
146/// JSON Patch 'add' operation representation
147#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
148#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
149#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
150pub struct AddOperation {
151    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
152    /// within the target document where the operation is performed.
153    #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))]
154    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
155    pub path: PointerBuf,
156    /// Value to add to the target location.
157    pub value: Value,
158}
159
160impl_display!(AddOperation);
161
162/// JSON Patch 'remove' operation representation
163#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
164#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
165#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
166pub struct RemoveOperation {
167    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
168    /// within the target document where the operation is performed.
169    #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))]
170    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
171    pub path: PointerBuf,
172}
173
174impl_display!(RemoveOperation);
175
176/// JSON Patch 'replace' operation representation
177#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
178#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
179#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
180pub struct ReplaceOperation {
181    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
182    /// within the target document where the operation is performed.
183    #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))]
184    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
185    pub path: PointerBuf,
186    /// Value to replace with.
187    pub value: Value,
188}
189
190impl_display!(ReplaceOperation);
191
192/// JSON Patch 'move' operation representation
193#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
194#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
195#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
196pub struct MoveOperation {
197    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
198    /// to move value from.
199    #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))]
200    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
201    pub from: PointerBuf,
202    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
203    /// within the target document where the operation is performed.
204    #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))]
205    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
206    pub path: PointerBuf,
207}
208
209impl_display!(MoveOperation);
210
211/// JSON Patch 'copy' operation representation
212#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
213#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
214#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
215pub struct CopyOperation {
216    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
217    /// to copy value from.
218    #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))]
219    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
220    pub from: PointerBuf,
221    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
222    /// within the target document where the operation is performed.
223    #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))]
224    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
225    pub path: PointerBuf,
226}
227
228impl_display!(CopyOperation);
229
230/// JSON Patch 'test' operation representation
231#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
232#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
233#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
234pub struct TestOperation {
235    /// JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location
236    /// within the target document where the operation is performed.
237    #[cfg_attr(feature = "schemars", schemars(schema_with = "String::json_schema"))]
238    #[cfg_attr(feature = "utoipa", schema(value_type = String))]
239    pub path: PointerBuf,
240    /// Value to test against.
241    pub value: Value,
242}
243
244impl_display!(TestOperation);
245
246/// JSON Patch single patch operation
247#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
248#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
249#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
250#[serde(tag = "op")]
251#[serde(rename_all = "lowercase")]
252pub enum PatchOperation {
253    /// 'add' operation
254    Add(AddOperation),
255    /// 'remove' operation
256    Remove(RemoveOperation),
257    /// 'replace' operation
258    Replace(ReplaceOperation),
259    /// 'move' operation
260    Move(MoveOperation),
261    /// 'copy' operation
262    Copy(CopyOperation),
263    /// 'test' operation
264    Test(TestOperation),
265}
266
267impl_display!(PatchOperation);
268
269impl PatchOperation {
270    /// Returns a reference to the path the operation applies to.
271    pub fn path(&self) -> &Pointer {
272        match self {
273            Self::Add(op) => &op.path,
274            Self::Remove(op) => &op.path,
275            Self::Replace(op) => &op.path,
276            Self::Move(op) => &op.path,
277            Self::Copy(op) => &op.path,
278            Self::Test(op) => &op.path,
279        }
280    }
281}
282
283impl Default for PatchOperation {
284    fn default() -> Self {
285        PatchOperation::Test(TestOperation::default())
286    }
287}
288
289/// This type represents all possible errors that can occur when applying JSON patch
290#[derive(Debug, Error)]
291#[non_exhaustive]
292pub enum PatchErrorKind {
293    /// `test` operation failed because values did not match.
294    #[error("value did not match")]
295    TestFailed,
296    /// `from` JSON pointer in a `move` or a `copy` operation was incorrect.
297    #[error("\"from\" path is invalid")]
298    InvalidFromPointer,
299    /// `path` JSON pointer is incorrect.
300    #[error("path is invalid")]
301    InvalidPointer,
302    /// `move` operation failed because target is inside the `from` location.
303    #[error("cannot move the value inside itself")]
304    CannotMoveInsideItself,
305}
306
307impl From<jsonptr::index::ParseIndexError> for PatchErrorKind {
308    fn from(_: jsonptr::index::ParseIndexError) -> Self {
309        Self::InvalidPointer
310    }
311}
312
313impl From<jsonptr::index::OutOfBoundsError> for PatchErrorKind {
314    fn from(_: jsonptr::index::OutOfBoundsError) -> Self {
315        Self::InvalidPointer
316    }
317}
318
319/// This type represents all possible errors that can occur when applying JSON patch
320#[derive(Debug, Error)]
321#[error("operation '/{operation}' failed at path '{path}': {kind}")]
322#[non_exhaustive]
323pub struct PatchError {
324    /// Index of the operation that has failed.
325    pub operation: usize,
326    /// `path` of the operation.
327    pub path: PointerBuf,
328    /// Kind of the error.
329    pub kind: PatchErrorKind,
330}
331
332fn translate_error(kind: PatchErrorKind, operation: usize, path: &Pointer) -> PatchError {
333    PatchError {
334        operation,
335        path: path.to_owned(),
336        kind,
337    }
338}
339
340fn add(doc: &mut Value, path: &Pointer, value: Value) -> Result<Option<Value>, PatchErrorKind> {
341    let Some((parent, last)) = path.split_back() else {
342        // empty path, add is replace the value wholesale
343        return Ok(Some(std::mem::replace(doc, value)));
344    };
345
346    let mut parent = doc
347        .pointer_mut(parent.as_str())
348        .ok_or(PatchErrorKind::InvalidPointer)?;
349
350    match &mut parent {
351        Value::Object(obj) => Ok(obj.insert(last.decoded().into_owned(), value)),
352        Value::Array(arr) => {
353            let idx = last.to_index()?.for_len_incl(arr.len())?;
354            arr.insert(idx, value);
355            Ok(None)
356        }
357        _ => Err(PatchErrorKind::InvalidPointer),
358    }
359}
360
361fn remove(doc: &mut Value, path: &Pointer, allow_last: bool) -> Result<Value, PatchErrorKind> {
362    let Some((parent, last)) = path.split_back() else {
363        return Err(PatchErrorKind::InvalidPointer);
364    };
365    let mut parent = doc
366        .pointer_mut(parent.as_str())
367        .ok_or(PatchErrorKind::InvalidPointer)?;
368
369    match &mut parent {
370        Value::Object(obj) => match obj.remove(last.decoded().as_ref()) {
371            None => Err(PatchErrorKind::InvalidPointer),
372            Some(val) => Ok(val),
373        },
374        // XXX: is this really correct? semantically it seems off, `-` refers to an empty position, not the last element
375        Value::Array(arr) if allow_last && matches!(last.to_index(), Ok(Index::Next)) => {
376            Ok(arr.pop().unwrap())
377        }
378        Value::Array(arr) => {
379            let idx = last.to_index()?.for_len(arr.len())?;
380            Ok(arr.remove(idx))
381        }
382        _ => Err(PatchErrorKind::InvalidPointer),
383    }
384}
385
386fn replace(doc: &mut Value, path: &Pointer, value: Value) -> Result<Value, PatchErrorKind> {
387    let target = doc
388        .pointer_mut(path.as_str())
389        .ok_or(PatchErrorKind::InvalidPointer)?;
390    Ok(std::mem::replace(target, value))
391}
392
393fn mov(
394    doc: &mut Value,
395    from: &Pointer,
396    path: &Pointer,
397    allow_last: bool,
398) -> Result<Option<Value>, PatchErrorKind> {
399    // Check we are not moving inside own child
400    if path.starts_with(from) && path.len() != from.len() {
401        return Err(PatchErrorKind::CannotMoveInsideItself);
402    }
403    let val = remove(doc, from, allow_last).map_err(|err| match err {
404        PatchErrorKind::InvalidPointer => PatchErrorKind::InvalidFromPointer,
405        err => err,
406    })?;
407    add(doc, path, val)
408}
409
410fn copy(doc: &mut Value, from: &Pointer, path: &Pointer) -> Result<Option<Value>, PatchErrorKind> {
411    let source = doc
412        .pointer(from.as_str())
413        .ok_or(PatchErrorKind::InvalidFromPointer)?
414        .clone();
415    add(doc, path, source)
416}
417
418fn test(doc: &Value, path: &Pointer, expected: &Value) -> Result<(), PatchErrorKind> {
419    let target = doc
420        .pointer(path.as_str())
421        .ok_or(PatchErrorKind::InvalidPointer)?;
422    if *target == *expected {
423        Ok(())
424    } else {
425        Err(PatchErrorKind::TestFailed)
426    }
427}
428
429/// Patch provided JSON document (given as `serde_json::Value`) in-place. If any of the patch is
430/// failed, all previous operations are reverted. In case of internal error resulting in panic,
431/// document might be left in inconsistent state.
432///
433/// # Example
434/// Create and patch document:
435///
436/// ```rust
437/// #[macro_use]
438/// use json_patch::{Patch, patch};
439/// use serde_json::{from_value, json};
440///
441/// # pub fn main() {
442/// let mut doc = json!([
443///     { "name": "Andrew" },
444///     { "name": "Maxim" }
445/// ]);
446///
447/// let p: Patch = from_value(json!([
448///   { "op": "test", "path": "/0/name", "value": "Andrew" },
449///   { "op": "add", "path": "/0/happy", "value": true }
450/// ])).unwrap();
451///
452/// patch(&mut doc, &p).unwrap();
453/// assert_eq!(doc, json!([
454///   { "name": "Andrew", "happy": true },
455///   { "name": "Maxim" }
456/// ]));
457///
458/// # }
459/// ```
460pub fn patch(doc: &mut Value, patch: &[PatchOperation]) -> Result<(), PatchError> {
461    let mut undo_stack = Vec::with_capacity(patch.len());
462    if let Err(e) = apply_patches(doc, patch, Some(&mut undo_stack)) {
463        if let Err(e) = undo_patches(doc, &undo_stack) {
464            unreachable!("unable to undo applied patches: {e}")
465        }
466        return Err(e);
467    }
468    Ok(())
469}
470
471/// Patch provided JSON document (given as `serde_json::Value`) in-place. Different from [`patch`]
472/// if any patch failed, the document is left in an inconsistent state. In case of internal error
473/// resulting in panic, document might be left in inconsistent state.
474///
475/// # Example
476/// Create and patch document:
477///
478/// ```rust
479/// #[macro_use]
480/// use json_patch::{Patch, patch_unsafe};
481/// use serde_json::{from_value, json};
482///
483/// # pub fn main() {
484/// let mut doc = json!([
485///     { "name": "Andrew" },
486///     { "name": "Maxim" }
487/// ]);
488///
489/// let p: Patch = from_value(json!([
490///   { "op": "test", "path": "/0/name", "value": "Andrew" },
491///   { "op": "add", "path": "/0/happy", "value": true }
492/// ])).unwrap();
493///
494/// patch_unsafe(&mut doc, &p).unwrap();
495/// assert_eq!(doc, json!([
496///   { "name": "Andrew", "happy": true },
497///   { "name": "Maxim" }
498/// ]));
499///
500/// # }
501/// ```
502pub fn patch_unsafe(doc: &mut Value, patch: &[PatchOperation]) -> Result<(), PatchError> {
503    apply_patches(doc, patch, None)
504}
505
506/// Undoes operations performed by `apply_patches`. This is useful to recover the original document
507/// in case of an error.
508fn undo_patches(doc: &mut Value, undo_patches: &[PatchOperation]) -> Result<(), PatchError> {
509    for (operation, patch) in undo_patches.iter().enumerate().rev() {
510        match patch {
511            PatchOperation::Add(op) => {
512                add(doc, &op.path, op.value.clone())
513                    .map_err(|e| translate_error(e, operation, &op.path))?;
514            }
515            PatchOperation::Remove(op) => {
516                remove(doc, &op.path, true).map_err(|e| translate_error(e, operation, &op.path))?;
517            }
518            PatchOperation::Replace(op) => {
519                replace(doc, &op.path, op.value.clone())
520                    .map_err(|e| translate_error(e, operation, &op.path))?;
521            }
522            PatchOperation::Move(op) => {
523                mov(doc, &op.from, &op.path, true)
524                    .map_err(|e| translate_error(e, operation, &op.path))?;
525            }
526            PatchOperation::Copy(op) => {
527                copy(doc, &op.from, &op.path)
528                    .map_err(|e| translate_error(e, operation, &op.path))?;
529            }
530            _ => unreachable!(),
531        }
532    }
533
534    Ok(())
535}
536
537// Apply patches while tracking all the changes being made so they can be reverted back in case
538// subsequent patches fail. The inverse of all state changes is recorded in the `undo_stack` which
539// can be reapplied using `undo_patches` to get back to the original document.
540fn apply_patches(
541    doc: &mut Value,
542    patches: &[PatchOperation],
543    undo_stack: Option<&mut Vec<PatchOperation>>,
544) -> Result<(), PatchError> {
545    for (operation, patch) in patches.iter().enumerate() {
546        match patch {
547            PatchOperation::Add(ref op) => {
548                let prev = add(doc, &op.path, op.value.clone())
549                    .map_err(|e| translate_error(e, operation, &op.path))?;
550                if let Some(&mut ref mut undo_stack) = undo_stack {
551                    undo_stack.push(match prev {
552                        None => PatchOperation::Remove(RemoveOperation {
553                            path: op.path.clone(),
554                        }),
555                        Some(v) => PatchOperation::Add(AddOperation {
556                            path: op.path.clone(),
557                            value: v,
558                        }),
559                    })
560                }
561            }
562            PatchOperation::Remove(ref op) => {
563                let prev = remove(doc, &op.path, false)
564                    .map_err(|e| translate_error(e, operation, &op.path))?;
565                if let Some(&mut ref mut undo_stack) = undo_stack {
566                    undo_stack.push(PatchOperation::Add(AddOperation {
567                        path: op.path.clone(),
568                        value: prev,
569                    }))
570                }
571            }
572            PatchOperation::Replace(ref op) => {
573                let prev = replace(doc, &op.path, op.value.clone())
574                    .map_err(|e| translate_error(e, operation, &op.path))?;
575                if let Some(&mut ref mut undo_stack) = undo_stack {
576                    undo_stack.push(PatchOperation::Replace(ReplaceOperation {
577                        path: op.path.clone(),
578                        value: prev,
579                    }))
580                }
581            }
582            PatchOperation::Move(ref op) => {
583                let prev = mov(doc, &op.from, &op.path, false)
584                    .map_err(|e| translate_error(e, operation, &op.path))?;
585                if let Some(&mut ref mut undo_stack) = undo_stack {
586                    if let Some(prev) = prev {
587                        undo_stack.push(PatchOperation::Add(AddOperation {
588                            path: op.path.clone(),
589                            value: prev,
590                        }));
591                    }
592                    undo_stack.push(PatchOperation::Move(MoveOperation {
593                        from: op.path.clone(),
594                        path: op.from.clone(),
595                    }));
596                }
597            }
598            PatchOperation::Copy(ref op) => {
599                let prev = copy(doc, &op.from, &op.path)
600                    .map_err(|e| translate_error(e, operation, &op.path))?;
601                if let Some(&mut ref mut undo_stack) = undo_stack {
602                    undo_stack.push(match prev {
603                        None => PatchOperation::Remove(RemoveOperation {
604                            path: op.path.clone(),
605                        }),
606                        Some(v) => PatchOperation::Add(AddOperation {
607                            path: op.path.clone(),
608                            value: v,
609                        }),
610                    })
611                }
612            }
613            PatchOperation::Test(ref op) => {
614                test(doc, &op.path, &op.value)
615                    .map_err(|e| translate_error(e, operation, &op.path))?;
616            }
617        }
618    }
619
620    Ok(())
621}
622
623/// Patch provided JSON document (given as `serde_json::Value`) in place with JSON Merge Patch
624/// (RFC 7396).
625///
626/// # Example
627/// Create and patch document:
628///
629/// ```rust
630/// #[macro_use]
631/// use json_patch::merge;
632/// use serde_json::json;
633///
634/// # pub fn main() {
635/// let mut doc = json!({
636///   "title": "Goodbye!",
637///   "author" : {
638///     "givenName" : "John",
639///     "familyName" : "Doe"
640///   },
641///   "tags":[ "example", "sample" ],
642///   "content": "This will be unchanged"
643/// });
644///
645/// let patch = json!({
646///   "title": "Hello!",
647///   "phoneNumber": "+01-123-456-7890",
648///   "author": {
649///     "familyName": null
650///   },
651///   "tags": [ "example" ]
652/// });
653///
654/// merge(&mut doc, &patch);
655/// assert_eq!(doc, json!({
656///   "title": "Hello!",
657///   "author" : {
658///     "givenName" : "John"
659///   },
660///   "tags": [ "example" ],
661///   "content": "This will be unchanged",
662///   "phoneNumber": "+01-123-456-7890"
663/// }));
664/// # }
665/// ```
666pub fn merge(doc: &mut Value, patch: &Value) {
667    if !patch.is_object() {
668        *doc = patch.clone();
669        return;
670    }
671
672    if !doc.is_object() {
673        *doc = Value::Object(Map::new());
674    }
675    let map = doc.as_object_mut().unwrap();
676    for (key, value) in patch.as_object().unwrap() {
677        if value.is_null() {
678            map.remove(key.as_str());
679        } else {
680            merge(map.entry(key.as_str()).or_insert(Value::Null), value);
681        }
682    }
683}