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

Method to get string representations of values #642

Closed
skaupper opened this issue Jun 29, 2017 · 18 comments
Closed

Method to get string representations of values #642

skaupper opened this issue Jun 29, 2017 · 18 comments

Comments

@skaupper
Copy link

skaupper commented Jun 29, 2017

I've come across occasions where I needed to "stringify" a single value.
I know that there is dump() to serialize the whole object but it doesn't work as I would expected it (string values are enclosed in double quotes). Actually I know dump() is not meant for representing a single value as string that's why I want to suggest to add a method responsible for converting the value to a string.

My own implementation that I'm currently using looks like following:

inline std::string to_string(const json &j)
{
    auto tmp = j.dump();
    
    if (j.type() == json::value_t::string) {
        return tmp.substr(1, tmp.size() - 2);
    } else {
        return tmp;
    }
}

So, basically I want code like this to work.

    json j = "{\"field\": 1234}"_json;
    std::string s = j["field"].to_string();

Could you imagine having such a method?
Maybe it is possible to specialize the get() method to get such a behaviour?

@theodelrieu
Copy link
Contributor

I understand the need, but I'm not sure we can modify the behavior of get, users may rely on strict type matching in every case.

We could add a templated method as<U>, that would retrieve the correct type and then attempt to convert to U.

But this can add confusion (remembering which of get and as is strict can be problematic).

@skaupper
Copy link
Author

I understand the need, but I'm not sure we can modify the behavior of get, users may rely on strict type matching in every case.

That sounds reasonable.

But this can add confusion (remembering which of get and as is strict can be problematic).

What if the new template method is named get_as<U>? Wouldn't that clearify its behaviour?

@theodelrieu
Copy link
Contributor

That seems correct.
So if U is json::string_t, we shall convert the value to a string, else, we just rely on regular type conversion?

It'd be great if you could experiment a bit with this, don't hesitate to open a PR!

We shall discuss this in more depth, with @nlohmann and others too.

@skaupper
Copy link
Author

So if U is json::string_t, we shall convert the value to a string, else, we just rely on regular type conversion?

The problem is that

json j = "{\"field\": 1234}"_json;
std::string s = j["field"].get_as<std::string>();

would work but code like

json j = "{\"field\": \"1234\"}"_json;
int i = j["field"].get_as<int>();

would not.

If we take the get_as<U> approach (instead of just as_string) we would need to implement both directions, to AND from string.
We would at least need to write specializations for all json::string -> primitive type conversions (get_as<uint32_t> and so on) and fall back to get<T> for other types.

I've just looked up the type_traits header. It seems like it is doable.

@theodelrieu
Copy link
Contributor

Indeed, the main issue I see if we go down that road, is that we must handle all cases (null as an int or string) etc...

I don't know what the JSON standard says about conversions, and @nlohmann knows a lot more about it than me.
This feature would add complexity to the implementation, so I'd like to have more people inputs about it before going further.

@gregmarr
Copy link
Contributor

This seems like a perfectly reasonable and simple helper function for a user to implement if they need this functionality. I don't know that it's important enough to put it in the main library, as it seems like a pretty specific need.

inline std::string to_string(const json &j)
{
    if (j.type() == json::value_t::string) {
        return j.get<std::string>();
    }

    return j.dump();
}

@skaupper
Copy link
Author

@gregmarr Thanks, I'll go with that for now.
Let's see what the maintainers say about that whole conversion thing.

@nlohmann nlohmann added kind: question state: please discuss please discuss the issue or vote for your favorite option labels Jun 29, 2017
@nlohmann
Copy link
Owner

Note that dump also escapes characters in the string.

I agree with @theodelrieu that a full-fledged "convert anything to anything" function would add unnecessary complexity to the library that makes it simpler to use by a few, but also harder to maintain and test. As @gregmarr pointed out, there is also no need for a library function as you can implement your own getter.

@nlohmann
Copy link
Owner

nlohmann commented Jul 6, 2017

Anything to be done here?

@nlohmann nlohmann removed the state: please discuss please discuss the issue or vote for your favorite option label Jul 8, 2017
@nlohmann nlohmann closed this as completed Jul 8, 2017
@Milerius
Copy link

Milerius commented Aug 3, 2019

any news for a get_as ?

@rnehra01
Copy link

rnehra01 commented Aug 5, 2021

@nlohmann A similar use case. Is it possible to store an integer/float as string instead of using std::stod/stoll for conversion (maybe for all interger/floats present in json string)?
E.g

json j = json::parse("{\"field\": 12.34}");
std::string  = j["field"];

It is needed because string to double conversion using stod cause precision loss.

@nlohmann
Copy link
Owner

nlohmann commented Aug 5, 2021

There is no precision loss when the number can be represented as double. See https://json.nlohmann.me/features/types/number_handling/

@rnehra01
Copy link

rnehra01 commented Aug 5, 2021

Yes, the issue is when number can't per represented precisely as double.

auto json = json_ld::parse(R"({"num":167233169.69999998808})"); // I get this from a server which store 18 decimal places of precision
auto data = json["num"];
std::cout<<std::to_string(data.get<double>()*100000); // 16723316969999.998047

// Now if I use long double then it is okay for this number but issue can still happen for a bigger number with higher precision
std::cout<<std::to_string(data.get<long double>()*100000); // 16723316969999.998808

// So I want to store it in string instead of long double

@nlohmann
Copy link
Owner

nlohmann commented Aug 5, 2021

No, this is not possible.

@Jack012a
Copy link

Jack012a commented Apr 8, 2022

I agree with rnehra01 that this function is needed when we handle a very large double while the conversion will lose precision.

@gelldur
Copy link

gelldur commented Sep 9, 2022

@nlohmann such possibility still should be possible. If my json contains invalid double value e.g. 0.00000000000000000000001, because some API printed it as fixed decimal, I still would like to parse it with my own fixed decimal approach.
Is there any way to do it? I just want to read any field as string if needed.

Not to mention that some bad API in case it wont fit in double will replace it by string. So api isn't stable with types. I know edge case. In general great lib.

@falbrechtskirchinger
Copy link
Contributor

falbrechtskirchinger commented Sep 9, 2022

@gelldur See #3432.

@falbrechtskirchinger
Copy link
Contributor

That should throw a runtime error.

I think the SAX API is the only way to do it at the moment and yes – it is quite involved.

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

No branches or pull requests

9 participants