相关文章推荐
求醉的小刀  ·  Qt Creator "The ...·  1 年前    · 
可爱的领带  ·  Qbi ...·  1 年前    · 
才高八斗的啄木鸟  ·  Maven ...·  2 年前    · 
Sign in

I'm trying to convert some code that was previously using boost::ptree. One problem I've run into is that this library doesn't seem to automatically convert from strings to bools or numbers.
For instance the following code:

json root = json::parse("{ my_value=\"true\" }");
bool value = root.at("my_value").get<bool>();

Unfortunately I have no control over the process that is producing the json, so there are cases where I know the value will be a bool or a number, but it may have been placed in quotes. Is there a smart way to handle this?

A possible workaround using a wrapper type with an implicit conversion operator:

struct Boolean {
    bool value;
    operator bool() const { return value; }
inline void to_json(json &j, const Boolean &b) {
    if (b.value) j = "true";
    else j = "false";
inline void from_json(const json &j, Boolean &b) {
    auto &str = j.get_ref<const std::string &>();
    if (str == "true") b.value = true;
    else if (str == "false") b.value = false;
    else ;// handle invalid value
int main(int, char **) {
    json root = json::parse("{ \"my_value\": \"true\" }");
    bool value = root.at("my_value").get<Boolean>();
    std::cout << value << "\n";
        

While that's not terrible for the bool case, it gets worse when dealing with numbers. It also means I'd have to essentially duplicate the code that actually parses the value into a typed value. So for example, var="1234" or var=1234. Both need the step of turning "1234" into an integer. One just happens to be wrapped in quotes. Sure I could just use stoi, but then that would fail if the number was specified with an exponent, var="3e8". It would make more sense to leverage all that parsing code that's already built in.

When the lexer encounters an opening quotation mark, it starts processing a string until the closing quotation mark. After that point, the type is fixed, and get<>() can only change it if there's a conversion function from JSON to the requested type.

You could re-use the built-in parsing logic by passing the string to json::parse(). That function will work with a singular value without an object or array.

Regarding floating-point numbers, the lexer tries std::stroll() and then std::strtof(). So you're not gaining that much. The only difference is, that the lexer validates the number and knows one of the two functions has to work.

You can still combine this with my original idea:

inline void from_json(const json &j, Boolean &b) {
    if(j.is_boolean())
        b.value = j.get<bool>();
    else {
        auto j2 = json::parse(j.get_ref<const std::string &>());
        b.value = j2.get<bool>();

This covers "key": true/false, as well as "key": "true"/"false". Numbers can be handled in a similar manner.