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

object.dump gives quoted string, want to use .dump() to generate javascripts. #826

Closed
coolhandle01 opened this issue Nov 10, 2017 · 19 comments

Comments

@coolhandle01
Copy link

coolhandle01 commented Nov 10, 2017

Feature Request

First up, amazing library. Love it. Definitely going to play well with V8 and V8PP, so well done.

  • What is the issue you have?
    My issue is that j.dump() returns a string in quotes for objects, and I have no way to disable this. I'd like to .dump() something I can build strings with using boost::format without having to write my own dequoting method and suddenly thinking about complexity of string ops. That's your job! ;P

  • Please describe the steps to reproduce the issue. Can you provide a small but working code example?
    Simplifying to Pseudo-esque code:

static auto matic= boost::format("var fn = function() { return %s };");

auto j = R"({ "some_object": {"happy": true, "pi": 3.141 } })"_json;
auto dumped_j = j.at("some_object").dump();
auto formatted = boost::str(matic % dumped_j ); 
// formatted == "var fn = function() { return \"<some_object_here>\"; }".
// ^^^^ formatted[0] and formatted[len-1] are '\"'
  • What is the expected behavior?
    I don't want the quotes on my string-dumped object (others probably do).. I'd like dump to be extended with default parameter bool bQuoted=true so I can set it false.
    auto formatted = boost::str(matic% j.at("some_object").dump(false)); //formatted == "var fn = function() { return <some_object_here>; }".
    // no quotes

  • And what is the actual behavior instead?
    I get the quotes, see above. see line 8505 in the header of 2.11

  • Which compiler and operating system are you using? Is it a supported compiler?
    MSVC 2015 on Win10 Pro

  • Did you use a released version of the library or the version from the develop branch?

  • If you experience a compilation error: can you compile and run the unit tests?
    I'm using 2.11 from the releases page

  • Describe the feature in as much detail as possible.
    make quotes surrounding dumped objects an option

  • Include sample usage where appropriate.
    auto str = j.dump(-1, false);

@nlohmann
Copy link
Owner

I am not sure whether producing non-compliant JSON text is in the scope of this library. However, I do not fully understand your use case. Could you please provide the exact desired output for the JSON value

{
    "some_object": {
        "happy": true,
        "pi": 3.141
    }
}

@nlohmann nlohmann added the state: needs more info the author of the issue needs to provide more details label Nov 10, 2017
@coolhandle01
Copy link
Author

coolhandle01 commented Nov 13, 2017

As JSON, I'd like it as it is - in C++ land, I'd like a string representation of the object without the surrounding quotes, so that I can inject it into the last %s here: boost::format("var %s = %s.%s(%s)")

I am building a JavaScript based on JSON data.
json.h -> C++ string representing an object -> string inserted into another string(via format/str) -> write to file.

@nlohmann
Copy link
Owner

So how would the JSON above look like?

@coolhandle01
Copy link
Author

coolhandle01 commented Nov 13, 2017

Sorry for inflicting confusion here!

The JSON is something like this:

{
    "Calls":
    [
        {
            "var": "returned_value",
            "fn": "named_method",
            "args":
            {
                "some_object": {
                    "happy": true,
                    "pi": 3.141
                 }
            }
        },
        ...
    ]
}

And I'd like to generate the following string as painlessly as possible, before I write it to some_script.js:

var returned_value = KnownThing.named_method({ "some_object": {"happy": true, "pi": 3.141 }});

@nlohmann
Copy link
Owner

So you want

{
    "some_object": {
        "happy": true,
        "pi": 3.141
    }
}

to become

{ "some_object": {"happy": true, "pi": 3.141 }}

?

@coolhandle01
Copy link
Author

coolhandle01 commented Nov 14, 2017

Nono, Indentation is not an issue or concern here.

When I call dump, I'd like
{ "some_object": {"happy": true, "pi": 3.141 }}
but I get
"{ "some_object": {"happy": true, "pi": 3.141 }}"
in fact in the debugger it looks like this:
\"{ "some_object": {"happy": true, "pi": 3.141 }}\" (last I checked, might be going insane on this project)
and so I have to dequote the string result of .dump() with some hacky method, before writing the said string to some .js.
Like:
auto js = boost::str(boost::format("var %s = %s.%s(%s)"), varname, objname, methodname, args);
where auto args = j.dump();

I'm really struggling to make this clearer!

@nlohmann
Copy link
Owner

How did you parse the JSON text? Did you use parse to did you write something like json j = "..."?

@gregmarr
Copy link
Contributor

It is impossible for the code you provided to produce the string "{ "some_object": {"happy": true, "pi": 3.141 }}" because your code dumps "some_object". Can you verify that the code you provided is the same as what you're using?

@coolhandle01
Copy link
Author

I've worked around it now, because I obviously can't have random symbols in the JSON (variables), so I'm representing var references as strings, and doing a search, so I'm no longer using j.dump() with boost::format. Let me prepare a standalone example to emulate what I was doing. I will return soon with code.

@nlohmann
Copy link
Owner

Any news on this?

@nlohmann nlohmann closed this as completed Dec 6, 2017
@neel
Copy link

neel commented Feb 27, 2019

I have a similar requirement. Keep the keys unquoted optionally. This can be done by passing a flag to the dump() function. Also I need a verbatim type value which will have string content but dump will not put quotes around it.

@nlohmann
Copy link
Owner

Without quotes, dump would produce invalid JSON text. If you want such a function, you can traverse the values yourself recursively and produce the desired result. I do not see this as part of the API.

@nlohmann nlohmann removed the state: needs more info the author of the issue needs to provide more details label Mar 10, 2019
@coolhandle01
Copy link
Author

Hi Niels, I've been meaning to get back to you, apologies for not doing so sooner.

In the end I traversed the values as suggested, and probably ended up with better code for what I was trying to achieve - essentially writing a script where blocks from the JSON are args to the methods (but didn't want to parse the JSON str on the other side of the JS to make the object).

I moved the goalposts by changing the API I was targeting to methods accepting individual args instead of objects, to make things easier.

I do think it's a bit wierd that the dump() method gives you escaped quotes though, as I've never seen a json file where the first character was a " - always { or [, but its fairly likely you've seen more exotic json that I.

Quote weirdness I can deal with, and that aside, this library is brilliant, and I am grateful for it :)

@nlohmann
Copy link
Owner

I do think it's a bit wierd that the dump() method gives you escaped quotes though, as I've never seen a json file where the first character was a " - always { or [, but its fairly likely you've seen more exotic json that I.

We only escape quotes inside strings, and this is required by the JSON specification, see

This is not weird, it is the only correct way to produce a valid JSON text.

@mlutken
Copy link

mlutken commented Mar 13, 2020

Perhaps you can use something along these lines.
Perhaps Niels can tell if this is the way to go:

/** Converts an json object to a string representation.
Similar to json.dump() but without the quotes.
Assumes that the object is not structured (ie. primitive).
For structured object a empty string is returned.

Special values like null, numbers etc. are converted to
string representation.
*/
std::string to_string(const nlohmann::json& object)
{
    if (object.is_structured()) return "";
    if (object.is_null()) return "null";
    else if (object.is_string()) return object.get<std::string>();
    else if (object.is_boolean()) return object.get<bool>() ? "true" : "false";
    else if (object.is_number_float()) return std::to_string(object.get<double>());
    else if (object.is_number_integer()) return std::to_string(object.get<long>());
    else if (object.is_number_unsigned()) return std::to_string(object.get<unsigned long>()); // Seems unused
    else if (object.is_number()) return std::to_string(object.get<double>()); // Seems unused
    else {
        // Fallback in case we forgot an if above!
        std::string s = object.dump();
        return s.substr(1, s.length() -2) + " FIXMENM";
    }
}

@nlohmann
Copy link
Owner

You could simplify this to code like

std::string to_string(const nlohmann::json& j)
{
    switch (j.type())
    {
        // avoid escaping string value
        case value_t::string:
            return j.get<std::string>();

        case value_t::array:
        case value_t::object:
            return "";

        // use dump() for all other value types
        default:
            return j.dump();
    }
}

@JDdeVilliers
Copy link

JDdeVilliers commented Oct 29, 2020

I've been having similar issues. I see this question being asked a few times, so didn't wish to open another ticket. This seemed like the appropriate place to ask (if there's still life here). Full example code

pos posarr[3];
char posname[16];
int i;

posarr[0].x = 10;
posarr[0].y = 11;
sprintf(posname, "pos1");
memcpy(posarr[0].name, posname, sizeof(posarr[0].name));

posarr[1].x = 20;
posarr[1].y = 21;
sprintf(posname, "pos2");
memcpy(posarr[1].name, posname, sizeof(posarr[1].name));

posarr[2].x = 30;
posarr[2].y = 31;
sprintf(posname, "pos3");
memcpy(posarr[2].name, posname, sizeof(posarr[2].name));

nlohmann::json positem, posxlist, posylist, posnamelist;
for (i = 0; i < 3; ++i)
{
	positem = posarr[i].x;
	posxlist.push_back(positem);
	positem = posarr[i].y;
	posylist.push_back(positem);
	positem = posarr[i].name;
	posnamelist.push_back(positem);
}

nlohmann::json allpos = {
	{"x", posxlist.dump()},
	{"y", posylist.dump()},
	{"name", posnamelist.dump()},
	{"items", i}
};

dlog_print(DLOG_DEBUG, scServiceLogTag, "EXAMPLE OUTPUT |%s|", allpos.dump().c_str());

outputs

EXAMPLE OUTPUT |{"items":3,"name":"[\"pos1\",\"pos2\",\"pos3\"]","x":"[10,20,30]","y":"[11,21,31]"}|

The string variables have \ escape characters (is this a separate issue?), and the arrays are wrapped as strings. What I'm looking for / expecting is

EXAMPLE OUTPUT |{"items":3,"name":["pos1","pos2","pos3"],"x":[10,20,30],"y":[11,21,31]}|

I've tried to replace .dump() when building allpos with get<std::string>() as suggested in some tickets, but the app crashes at runtime, possibly because it's not actually a string, but a JSON array? (Tizen 4.0 Wearable - if that's relevant)

@nlohmann
Copy link
Owner

Just don't call dump, so the value remains of type json. It is then properly escaped.

@JDdeVilliers
Copy link

@nlohmann , oh my gosh. Perfect. Thank you so much.
And also, I'm really impressed by both the project and your amazing support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants