简体   繁体   中英

How to format {} in a json string using fmt?

I am trying to format a JSON string avoiding using fmt::format because json does include {0}'s and fmt cannot distinguish JSON's bracket from format specifier {0}'s brackets. If use fmt::format i get the following error:

terminate called after throwing an instance of 'fmt::v7::format_error'
  what():  invalid format string
Aborted (core dumped)

This is why I try and use fmt::sprintf . I could not find a helpful solution for c++ but i've seen a Golang solution but it did not work which is: fmt.Sprintf("%[2]d %[1]d\n", 11, 22) (of course I've changed it to fmt::sprintf).

How can I give argument index to a format specifier using fmt::sprintf?

Thanks in advance.

EDIT:

JSONs are not generated by me but sqlite. That json includes a {0} and it is not being formatted because of the outer JSON braces.

std::string json = fmt::format("{{ \"key\": [{}, {}] }}", 42, 17);
std::cout << json << std::endl; // prints { "key": [17, 42] }

Demo: https://godbolt.org/z/4KjTPq4T5

The convention fmt chose for escaping curly brackets is {{ and }} . A backslash, eg \{ , won't work. While it's common to see double bracketing for string interpolation, eg {{ some_var }} , the use of double bracketing as an escape sequence is somewhat unique to cpp (see cppformat ).

I sometimes use fmt to format small JSON snippets too. The other comments about the pitfalls of doing this are valid, but sometimes it's not a critical component and it gets the job done.

From the fmt documentation:

https://fmt.dev/latest/api.html#printf-api

The header fmt/printf.h provides printf-like formatting functionality. The following functions use printf format string syntax with the POSIX extension for positional arguments

That positional argument extension is described here:

https://en.wikipedia.org/wiki/Printf_format_string#Parameter_field

You can %n$... instead of %... , with n repaced with the argument you'd like to use.

For example:

std::string json = fmt::sprintf(R"({ "key": [%2$d, %1$d] })", 42, 17);
std::cout << json << std::endl; // prints { "key": [17, 42] }

Demo: https://godbolt.org/z/9dd734hxG

You question isn't very clear if I have to be honest, but I am almost sure this is a classic example of an XY problem.

IMHO, the main mistake is is that you are trying to format JSON by hand, which is by experience quite tricky and error prone.

Unless you are actually operating on a very small and strict set of inputs, there's an almost 100% certainty you will generate malformed JSON outputs in some cases. For instance, you have to also remember that all strings should be valid UTF-8, which means you have to either check your inputs or escape any weird character they might contain. You also have to escape anything that is not valid in a JSON string, such as " , like specified by the JSON standard .

This is why I think generating JSON by hand should only be done as a solution of last resort, ie if you have an embedded device and you can't afford to use any kind of library due to extremely severe memory constraints.

One library I often recommend is nlohmann/json , which might not be the fastest around 1 but it is definitely one of the easiest to use. This is mostly due to it being header-only library and the fact that nlohmann::json is compatible with STL algorithms and iterators (it basically behaves like a std::map or vector).

You can then do very neat things, such as:

#include <iostream>
#include <string_view>
#include <unordered_set>

#include <nlohmann/json.hpp>

int main() {
    const std::unordered_set<std::string_view> s { "a", "b", "c" };

    nlohmann::json jarr {};

    for (const auto sv : s) {
        jarr.push_back(sv);
    }

    nlohmann::json jv { { "values", std::move(jarr) } };

    std::cout << jv.dump() << '\n';

    return 0;
}

This prints {"values":["c","b","a"]} .

You can even straight out serialize and deserialize arbitrary types (even user defined ones) as a JSON:

#include <iostream>
#include <string_view>
#include <unordered_set>

#include <nlohmann/json.hpp>

int main() {
    const std::unordered_set<std::string_view> s { "a", "b", "c" };

    nlohmann::json jv { { "values", s } };

    std::cout << jv.dump() << '\n';

    return 0;
}

1 : In my experience, unless you must parse MILLIONS of JSONs per second, most performance considerations become moot. I've seen nlohmann's JSON library in use on the ESP32 and even there it was fast enough for light to moderate JSON parsing and generation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM