Code Monkey home page Code Monkey logo

Comments (5)

idubrov avatar idubrov commented on May 27, 2024 1

I wrote my own implementation of both JSON Patch and JSON Merge Patch a while ago: https://github.com/idubrov/json-patch

It tries to be optimal and modify Value in-place and revert changes if one of the operations fails.

from json.

dtolnay avatar dtolnay commented on May 27, 2024 1

The json-patch crate looks great! I am happy pointing people to the implementation there -- I don't think we need this to be provided by serde_json. Thanks all!

from json.

dtolnay avatar dtolnay commented on May 27, 2024

Copying this from IRC so it doesn't get lost:

<Cryptarchy> I have a library design quandary, if anyone would like to offer an opinion. I'm attempting to implement JSON Patch into serde_json.
<Cryptarchy> And I've got the general functionality just about done. But now I've run into the interesting circular problem that JSON Patches are JSON objects. So I'd need to convert them into something else in order to operate on them.
<Cryptarchy> I could just use from_str to get serde_json::Value structs to work with, and just use the values out of the map directly, but that seems kind of a hack.
<Cryptarchy> On the other hand, deserializing JSON into an object would require higher level bits of serde, which aren't available in serde_json. Or are they? That's what I'm unsure of: how to treat the string JSON object.

From serde_json's perspective I don't think we want to treat JSON patches as strings (i.e. dealing with from_str). If we just expose a type like serde_json::Patch and implement Serialize and Deserialize for Patch, the user's crate can handle serialization in the way that makes the most sense for them. For example they may want to embed a JSON patch into a larger struct, or deserialize from bytes instead of str, or use other generic deserialization machinery in their crate, or etc.

We can expose Patch and we can expose a function or method to apply a Patch to a serde_json::Value. I don't know JSON patch well enough to know what makes the most sense semantically but something like serde_json::Value::apply_patch(Patch) or serde_json::Patch::apply_to(Value) or serde_json::apply_patch(Patch, Value).

@CryptArchy does that approach address your concern about needing "higher level bits of serde"?

from json.

CryptArchy avatar CryptArchy commented on May 27, 2024

Yes, I think that all makes sense. I currently have a Command enum with the different ops, which I may rename as Patch or maybe Patch will be a type alias for Vec<Command>. And there's a apply_patch method on Values (currently in a Patcher trait) that mutates the Value according to the op(s).

I'll look into using the general (de)serialization traits and how to implement them for the final Patch type, that sounds like the way to go.


The trickiest part so far has been meeting the demands of atomicity required for HTTP Patch semantics. The RFC isn't totally clear if that's required by the implementation itself or if the library user should respect those semantics. I opted to implement it so the library is easier to use. If any part of the patch fails, the whole thing has no effect.

I could think of two obvious ways to do it:

  1. copy the Value first and if the patch fails, replace the original with the copy
  2. generate the inverse command and build an undo stack that can be executed to rollback

I went with the second option, though more complicated, it seems like it should be more efficient (needs testing). As a nice side benefit, you can also return the undo stuck so every application of a patch gives you the commands to revert it yourself if there's a need from a higher level.

It also creates a pleasing symmetry and flow in the execution. For example, Removeing a value gives ownership of that value, which is then consumed by creating an Add command to the same path which would put the value back. Adding a value can replace an existing value, so sometimes the inverse of an Add is a Replace with the old value in it. Errors return the faulty value. Essentially, no data is ever lost, just shuffled around and it's all enforced by the ownership system. It's a pretty uniquely Rustic version of JSON Patch! I'll post the code up soon to start review, though I have a lot of cleanup to do yet, so a PR is still a little ways off.

from json.

CryptArchy avatar CryptArchy commented on May 27, 2024

Alright, I cleaned up things a little and pushed some code up to my fork.

https://github.com/serde-rs/json/compare/master...CryptArchy:patch?expand=1

It's very rough as I'm still iterating on the concepts, but all the ops are in and working.

I added one non-standard op _Bump to act as the inversion of the Move op. Because of the annoying semantics where Add can either add or replace a value, any ops that rely on Add-like behavior get complicated. Move is the trickiest, as it always removes one value and then may create or replace a value. So reverting it requires either an inverted Move (if a new value was added) or a Move and an Add (if an existing value was replaced).

Bump is equivalent (and implemented as) a Replace then an Add. I could return a Vec or tuple of Commands from patch_move but then you need special handling for apply_patch and everything else to flatten it.

Currently, I'm thinking that the system could just convert _Bump into the equivalent commands before returning to the user, so that it's just an internal convenience. Since variants can't be private, I think I'd need a second Command enum that doesn't have it or something like that. I'm not sure yet, tricky stuff!

from json.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.