Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON Patch for Array Elements #2994

Closed
2 of 5 tasks
deepakd82 opened this issue Sep 1, 2021 · 6 comments
Closed
2 of 5 tasks

JSON Patch for Array Elements #2994

deepakd82 opened this issue Sep 1, 2021 · 6 comments
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@deepakd82
Copy link

deepakd82 commented Sep 1, 2021

Hi Team,
We are using nlohmann in our project. I have a query (as I am not aware about if this is actually a bug) on adding an array element.
1> Does the indexing is required ?
2> Should the value be in [ ]

What is the issue you have?

As per RFC 6902, we see the below information -

An example target JSON document:

   { "foo": [ "bar", "baz" ] }

A JSON Patch document:

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

The resulting JSON document:

   { "foo": [ "bar", "qux", "baz" ] }

But when we see example for json patch, we see below -

 { "op": "add", "path": "/hello", "value": ["world"] },

--> Don't see any index mentioned and the value is put inside [ ]

We have an array element with 4 values. But when we add the third element, we are observing the below error -

Failed to parse message: [json.exception.out_of_range.401] array index 2 is out of range, exception id:"}
The below statement is throwing the above error -

json json_patched_doc = bsc_json.patch(config_patch_as_json);

Please describe the steps to reproduce the issue.

Can you provide a small but working code example?

In our setup, we are receiving the request from GUI as below -

{
 "op":"add",
 "path":"/supported-param/1",
  "value":"ONE"
}

But if the request comes as below, we see error -

{
 "op":"add",
 "path":"/supported-param/2",
  "value":"TWO"
}
Failed to parse message: [json.exception.out_of_range.401] array index 2 is out of range, exception id:"}

What is the expected behavior?

And what is the actual behavior instead?

Which compiler and operating system are you using?

CentOS 7.6

  • Compiler: ___GCC 4.8.5 20150623
  • Operating system: ___ CENTOS7.6

Which version of the library did you use?

version 3.9.1

  • latest release version 3.10.2
  • other release - please state the version: ___ 3.9.1
  • the develop branch

If you experience a compilation error: can you compile and run the unit tests?

  • yes
  • no - please copy/paste the error message below
@nlohmann
Copy link
Owner

nlohmann commented Sep 1, 2021

I would need to understand your examples better. This is what I tried:

#include <iostream>

#include "json.hpp"

using json = nlohmann::json;

int main()
{
    json target1 = R"({ "foo": [ "bar", "baz" ] })"_json;
    json patch1 = R"([ { "op": "add", "path": "/foo/1", "value": "qux" } ])"_json;
    json patch2 = R"([ { "op": "add", "path": "/hello", "value": ["world"] } ])"_json;
    
    std::cout << target1.patch(patch1) << std::endl;
    std::cout << target1.patch(patch2) << std::endl;
    
    json target2 = R"( {"supported-param": ["one", "two", "three", "four"]} )"_json;
    json patch3 = R"([ { "op":"add", "path":"/supported-param/1", "value":"ONE" } ])"_json;
    json patch4 = R"([ { "op":"add", "path":"/supported-param/2", "value":"TWO" } ])"_json;

    std::cout << target2.patch(patch3) << std::endl;
    std::cout << target2.patch(patch4) << std::endl;
}

Output:

{"foo":["bar","qux","baz"]}
{"foo":["bar","baz"],"hello":["world"]}
{"supported-param":["one","ONE","two","three","four"]}
{"supported-param":["one","two","TWO","three","four"]}

@nlohmann nlohmann added the state: needs more info the author of the issue needs to provide more details label Sep 1, 2021
@deepakd82
Copy link
Author

deepakd82 commented Sep 1, 2021

@nlohmann
Thanks for your quick response.

Please find the code snippet that is being validated -

myjson.json

{
   "myPath": {
       "myParam": [
           "FOUR",
           "FIVE"
        ]
    }
}
using json = nlohmann::json;
readPatch( )
{
	std::string input = "{"op":"add","path":"/myPath/myParam","value":"ONE"}";
	json bsc_json;

	/* This is reading from a file (myjson.json) */
	readFullJson(bsc_json);

	const auto patch_as_json = json::parse(input);

	json json_patched_doc = bsc_json.patch(patch_as_json);

	for (auto& el : config_patch_as_json.items())
	{
        	 std::cout << "------------" << std::endl;
   	      	 std::cout << "key: " << el.key() << ", op:" << el.value()["op"] << std::endl;
        	 std::cout << "key: " << el.key() << ", path:" << el.value()["path"] << std::endl;
      	  	 std::cout << "key: " << el.key() << ", value:" << el.value()["value"] << std::endl;
	}
}

My query was,

1> Should nlohmann library expects the value in [ ] ?
2> Does nlohmann library doesn't need any index in the path ?

@nlohmann
Copy link
Owner

nlohmann commented Sep 1, 2021

I still do not understand your exact problem. I would need a concrete (and complete) code example and a description where the behavior of the library deviates from your expectations.

As for your questions: The library implements JSON Patch as specified in RFC 6902. For value, any JSON value can be added. The RFC describes the way the path is treated for add operations, see https://datatracker.ietf.org/doc/html/rfc6902#section-4.1.

@deepakd82
Copy link
Author

deepakd82 commented Sep 1, 2021

#include  <iostream>
#include  <fstream>
#include  <nlohmann/json.hpp>
#include  <string>

using json = nlohmann::json;

#define JSON_FILE_PATH "myJson.json"

json bsc_json;

bool readFullJson()
{
    std::ifstream file;
    file.open(JSON_FILE_PATH);

    if(!file)
    {
        //file does not exists
        std::cout << "File not present  "<< CONFIG_FILE_PATH << std::endl;
        return false;
    }

    auto result = false;

    bsc_json = nlohmann::json::parse(file);

    std::cout << " ------->> " << bsc_json.dump(4) << std::endl;

    return true;
}
void replaceAll(std::string& str, const std::string& from, const std::string& to)
{
    if (from.empty())
        return;

    size_t start_pos = 0;

    while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
    }
}

void applyPatch(std::string input)
{
    const auto patch_as_json = json::parse(input);
    const auto patch_input_as_json = patch_as_json["patch"];

    std::cout << " ------->> " << patch_input_as_json.dump(4) << std::endl;

    const auto patch_input_as_string = patch_input_as_json.dump();
    std::string patch_input_as_string_after_trimming = patch_input_as_string;
    if(patch_input_as_string.at(0) == '\"')
    {
       patch_input_as_string_after_trimming = patch_input_as_string.substr(1, patch_input_as_string.size() - 2);
    }
    replaceAll(patch_input_as_string_after_trimming, "\\\"", "\"");
    const auto patch_input_as_valid_json_string = patch_input_as_string_after_trimming;
    const auto patch_json = json::parse(patch_input_as_valid_json_string);

    json json_patched_doc = bsc_json.patch(patch_json);

    std::cout << " ------->> " << json_patched_doc.dump(4) << std::endl;
}

int main()
{
    std::string input = "{\"patch\": [{\"op\":\"add\",\"path\":\"/myPath/myParam/2\",\"value\":\"ONE\"}]\n}";

    if( true != readFullJson())
        return -1;

    applyPatch(input);

    return 0;
}

Output:

 ------->> {
    "myPath": {
        "myParam": [
            "FOUR",
            "FIVE"
        ]
    }
}
 ------->> [
    {
        "op": "add",
        "path": "/myPath/myParam/2",
        "value": "ONE"
    }
]
 ------->> {
    "myPath": {
        "myParam": [
            "FOUR",
            "FIVE",
            "ONE"
        ]
    }
}

My query here is :

In path, if I don't give the index, it will not work.

 std::string input = "{\"patch\": [{\"op\":\"add\",\"path\":\"/myPath/myParam\",\"value\":\"ONE\"}]\n}";
 ------->> {
    "myPath": {
        "myParam": [
            "FOUR",
            "FIVE"
        ]
    }
}
 ------->> [
    {
        "op": "add",
        "path": "/myPath/myParam",
        "value": "ONE"
    }
]
 ------->> {
    "myPath": {
        "myParam": "ONE"
    }
}

But its confirmed that the value can come with (or) without [ ] and this will not matter for adding an element to the array.

@nlohmann
Copy link
Owner

nlohmann commented Sep 1, 2021

Thanks. The behavior looks as expected to me.

From RFC 6902, Sect. 4.2:

The "add" operation performs one of the following functions,
depending upon what the target location references:

o If the target location specifies an array index, a new value is
inserted into the array at the specified index.

o If the target location specifies an object member that does not
already exist, a new member is added to the object.

o If the target location specifies an object member that does exist,
that member's value is replaced.

When you use /myPath/myParam/2 as path, then you are in the first bullet point, and

a new value is inserted into the array at the specified index.

In your case, the value was ["FOUR", "FIVE"], and the index is 2. The RFC further states

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.

So the value will be appended, resulting in ["FOUR", "FIVE", "ONE"].

Note when you drop the index and use /myPath/myParam, you are in the third bullet and just replace the value - just as you experienced.

The value given in the patch will be used as is - it's type does not matter. You can add array brackets if you want to add an array, but this has nothing to do with the place the value is added.

@nlohmann nlohmann added solution: proposed fix a fix for the issue has been proposed and waits for confirmation kind: question and removed kind: bug state: needs more info the author of the issue needs to provide more details labels Sep 1, 2021
@deepakd82
Copy link
Author

@nlohmann
thanks for your time and support.

@nlohmann nlohmann closed this as completed Sep 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation
Projects
None yet
Development

No branches or pull requests

2 participants