danielaparker / jsoncons

A C++, header-only library for constructing JSON and JSON-like data formats, with JSON Pointer, JSON Patch, JSON Schema, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSON
https://danielaparker.github.io/jsoncons
Other
697 stars 160 forks source link

apply_patch() request to add flexibility #500

Closed andriichA closed 4 months ago

andriichA commented 4 months ago

Hi, recently I discovered that add op doesn't work when using apply_patch() (jsoncons/include/jsoncons_ext/jsonpatch/jsonpatch.hpp) function. I was debugging it and the problem lies in add_if_absent() due to hardcoded "false" value for create_if_missing arg.

No error; applied patch

Error; patch hasn't been applied

Enumerate the steps to reproduce the bug

  1. Call apply_patch() in order to add a new json pointer-value with "add" op
  2. Observe the error

Include a small, self-contained example if possible When I try to add a new json pointer-value with "add" op the function add_if_absent() (inside apply_patch()) with create_if_missing = false is called. Then resolve() is called where the new json pointer isn't found in "current" json and since create_if_missing is false the error is returned.

What compiler, architecture, and operating system?

What jsoncons library version?

andriichA commented 4 months ago

Screenshot (16)

andriichA commented 4 months ago

So the only way for this to work is to check the root json and allocate new pointers by myself before I call apply_patch(), which is a waste if this lib already has this logic

When I've changed false value to true everything was fine (see the screenshot)

danielaparker commented 4 months ago

jsonpatch::apply_patch implements the JSONPatch RFC. The "add" operation only adds members to existing objects, it won't create new empty objects.

So, if your data is,

{ "foo": "bar"}

you can apply this patch

[
    { "op": "add", "path": "/baz", "value": "qux" }
]

but not this one

[
    { "op": "add", "path": "/baz/new", "value": "qux" }
]

Automatically adding the missing objects can be helpful, even if not allowed by JSONPatch. That's why we added some jsonpointer functions with the create_if_missing option, so you can do it as follows:

#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
#include <iostream>

using jsoncons::json;
namespace jsonpointer = jsoncons::jsonpointer;

int main()
{
    json data = json::parse(R"(
{ "foo": "bar"}
)");

    jsonpointer::add(data, "/baz/new", "qux", true);
    std::cout << jsoncons::pretty_print(data) << "\n";
}

Output:

{
    "baz": {
        "new": "qux"
    },
    "foo": "bar"
}
andriichA commented 4 months ago

Thank you for the explanation!

andriichA commented 4 months ago

Hi, Is there a way to implement apply_patch() func with an argument where I could specify If I want to create the missing pointers?

I would like to avoid redundant code since we are using your beautiful lib and don't just copy the apply_patch() logic but with true val for create_if_missing arg

Thank you!

danielaparker commented 4 months ago

apply_patch implements the JSONPatch standard, RFC6902, it can't deviate from that. The way to supply missing name-object pairs through JSONPatch is to include multiple add operations explicitly in the patch.