serde-patch
JSON Merge Patch (RFC 7396) for Serde-derived types.
Provides macros to generate partial patches (diff) and apply them immutably or in-place.
[dependencies]
serde-patch = "0.1.0"
Features
diff! – generates a minimal patch containing only changed fields (and optional forced fields).
apply! – consumes the current value and returns an updated one.
apply_mut! – modifies the current value in-place.
The patch can be any type that implements AsRef<[u8]> (&str, String, Vec<u8>, etc.).
Example
use serde::{Deserialize, Serialize};
use serde_patch::{apply, apply_mut, diff};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
struct Profile {
bio: String,
avatar_url: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
struct User {
id: u32,
username: String,
age: u8,
active: bool,
profile: Option<Profile>,
}
fn main() {
let old = User {
id: 1001,
username: "alice".to_string(),
age: 30,
active: true,
profile: Some(Profile {
bio: "Software engineer".to_string(),
avatar_url: Some("https://example.com/alice-old.jpg".to_string()),
}),
};
let new = User {
id: 1001,
username: "alice".to_string(),
age: 31,
active: false,
profile: Some(Profile {
bio: "Senior software engineer".to_string(),
avatar_url: None,
}),
};
let basic_patch = serde_json::to_string(&diff!(&old, &new).unwrap()).unwrap();
let forced_patch = serde_json::to_string(&diff!(&old, &new; ["id", "profile.bio"]).unwrap()).unwrap();
let updated = apply!(old.clone(), &basic_patch).unwrap();
assert_eq!(updated, new);
let mut current = old;
apply_mut!(&mut current, &forced_patch).unwrap();
assert_eq!(current, new);
}
Macros
diff!(old, new) – basic diff (only changed fields).
diff!(old, new; ["path.to.field", ...]) – include forced fields even if unchanged.
apply!(current, patch) – immutable.
apply_mut!(&mut current, patch) – mutable.