json icon indicating copy to clipboard operation
json copied to clipboard

Rel 3.11.2 -- patch method throw exception which use to work fine in Rel 3.9.1

Open sujayran opened this issue 1 year ago • 4 comments

Description

Code sample std::string valStr = jsonvalprofile.serialize(); nlohmann::json profileval = nlohmann::json::parse(valStr);

                             std::string valStr1 = jsonvalpatch.serialize();
                            nlohmann::json patchval = nlohmann::json::parse(valStr1);

                           // patch the original json value with patch object
                           result = profileval.patch(patchval);

Reproduction steps

sample json { "capacity": 50, "locality": "test", "id": "94f250ab-f6f5-46fa-8d38-765b5ce9ab6c", "xyz": [ { "lmn": "214", "nnp": "001" } ], "priority": 1 }

patch sample[ { "op" : "add", "path" : "/xyz/1/lmn", "value" : "214" }, { "op" : "add", "path" : "/xyz/1/nnp", "value" : "002" } ]

The patch operation fails with [json.exception.out_of_range.401] array index 1 is out of range

Expected vs. actual results

Exapected:

patch to be successful as it use to happen in v3.9.1

Minimal code example

std::string valStr = jsonvalprofile.serialize();
                                nlohmann::json profileval = nlohmann::json::parse(valStr);

                                 std::string valStr1 = jsonvalpatch.serialize();
                                nlohmann::json patchval = nlohmann::json::parse(valStr1);

                               // patch the original json value with patch object
                               result = profileval.patch(patchval);

Error messages

The patch operation fails with 
 [json.exception.out_of_range.401] array index 1 is out of range

Compiler and operating system

clang --version clang version 15.0.7 (Fedora 15.0.7-2.fc37)

Library version

v3.11.2

Validation

sujayran avatar Feb 13 '24 22:02 sujayran

Your sample JSON is invalid. It contains an unmatched ].

, } ], "xyz" : [ { "

Removing the }] makes it parsable.

gregmarr avatar Feb 14 '24 09:02 gregmarr

Your operation is invalid according to JSON Patch. Previous versions of the library erroneously accepted it. That was fixed in #3628

https://datatracker.ietf.org/doc/html/rfc6902#page-4

When the operation is applied, the target location MUST reference one of:

  • A member to add to an existing object - whereupon the supplied value is added to that object at the indicated location. If the member already exists, it is replaced by the specified value.
  • An element to add to an existing array - whereupon the supplied value is added to the array at the indicated location. Any elements at or above the specified index are shifted one position to the right. The specified index MUST NOT be greater than the number of elements in the array. If the "-" character is used to index the end of the array (see [RFC6901]), this has the effect of appending the value to the array.

Your location is neither an existing object, nor an array element, but instead a location inside a non-existing object. You need to add the full new element as a single operation, or add an empty element and then add members to it.

gregmarr avatar Feb 14 '24 09:02 gregmarr

Thank for the reply, if i understand this right, you are saying its failing due to the fact that at location of array element 1 we don't have lmn "/xyz/1/lmn" ?

{
  "op" : "add",
  "path" : "/xyz/1",
  "value" : ""
}, {
  "op" : "add",
  "path" : "/xyz/1/lmn",
  "value" : "214"
}, {
  "op" : "add",
  "path" : "/xyz/1/nnp",
  "value" : "002"
} 

/usr/json-3.11.3/include/nlohmann/json.hpp:4799: auto nlohmann::basic_json<>::patch_inplace(const basic_json<> &)::(anonymous class)::operator()(json_pointer &, basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>) const [ObjectType = std::map, ArrayType = std::vector, StringType = std::basic_string, BooleanType = bool, NumberIntegerType = long, NumberUnsignedType = unsigned long, NumberFloatType = double, AllocatorType = std::allocator, JSONSerializer = nlohmann::adl_serializer, BinaryType = std::vector, CustomBaseClass = void]: Assertion `false' failed.

sujayran avatar Feb 14 '24 15:02 sujayran

It's because /xyz/1 doesn't exist. You need to create /xyz/1 before you can add more things to it. What you have there is should be close to working but I think "value": "" would make it a string instead of an object. Can you try "value": {} instead?

Another way would be

{
  "op" : "add",
  "path" : "/xyz/1",
  "value" : { "lmn": "214", "nnp": "002" }
}

gregmarr avatar Feb 16 '24 16:02 gregmarr