I'm having a very simple program that outputs simple JSON string that I manually concatenate together and output through the std::cout stream (the output really is that simple) but I have strings that could contain double-quotes, curly-braces and other characters that could break the JSON string. So I need a library (or a function more accurately) to escape strings accordingly to the JSON standard, as lightweight as possible, nothing more, nothing less.

I found a few libraries that are used to encode whole objects into JSON but having in mind my program is 900 line cpp file, I rather want to not rely on a library that is few times bigger then my program just to achieve something as simple as this.

推荐答案

Caveat

Whatever solution you take, keep in mind that the JSON standard requires that you escape all control characters. This seems to be a common misconception. Many developers get that wrong.

All control characters表示从'\x00''\x1f'的所有内容,而不仅仅是'\x0a'(也称为'\n')这样的简短表示.例如,您将'\x02'个字符的must escape表示为\u0002.

另请参阅:ECMA-404 - The JSON data interchange syntax, 2nd edition, December 2017, Page 4

Simple solution

如果您确信您的输入字符串是UTF-8编码的,那么您可以让事情变得简单.

由于JSON允许您通过\uXXXX,甚至"\转义所有内容,因此一个简单的解决方案是:

#include <sstream>
#include <iomanip>

std::string escape_json(const std::string &s) {
    std::ostringstream o;
    for (auto c = s.cbegin(); c != s.cend(); c++) {
        if (*c == '"' || *c == '\\' || ('\x00' <= *c && *c <= '\x1f')) {
            o << "\\u"
              << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(*c);
        } else {
            o << *c;
        }
    }
    return o.str();
}

Shortest representation

对于最短的表示,可以使用JSON快捷方式,例如\"而不是\u0022.以下函数生成UTF-8编码字符串s的最短JSON表示:

#include <sstream>
#include <iomanip>

std::string escape_json(const std::string &s) {
    std::ostringstream o;
    for (auto c = s.cbegin(); c != s.cend(); c++) {
        switch (*c) {
        case '"': o << "\\\""; break;
        case '\\': o << "\\\\"; break;
        case '\b': o << "\\b"; break;
        case '\f': o << "\\f"; break;
        case '\n': o << "\\n"; break;
        case '\r': o << "\\r"; break;
        case '\t': o << "\\t"; break;
        default:
            if ('\x00' <= *c && *c <= '\x1f') {
                o << "\\u"
                  << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(*c);
            } else {
                o << *c;
            }
        }
    }
    return o.str();
}

Pure switch statement

It is also possible to get along with a pure switch statement, that is, without if and <iomanip>. While this is quite cumbersome, it may be preferable from a "security by simplicity and purity" point of view:

#include <sstream>

std::string escape_json(const std::string &s) {
    std::ostringstream o;
    for (auto c = s.cbegin(); c != s.cend(); c++) {
        switch (*c) {
        case '\x00': o << "\\u0000"; break;
        case '\x01': o << "\\u0001"; break;
        ...
        case '\x0a': o << "\\n"; break;
        ...
        case '\x1f': o << "\\u001f"; break;
        case '\x22': o << "\\\""; break;
        case '\x5c': o << "\\\\"; break;
        default: o << *c;
        }
    }
    return o.str();
}

Using a library

You might want to have a look at https://github.com/nlohmann/json, which is an efficient header-only C++ library (MIT License) that seems to be very well-tested.

You can either call their escape_string() method directly (Note that this is a bit tricky, see comment below by Lukas Salich), or you can take their implementation of escape_string() as a starting point for your own implementation:

https://github.com/nlohmann/json/blob/ec7a1d834773f9fee90d8ae908a0c9933c5646fc/src/json.hpp#L4604-L4697

Json相关问答推荐

如何使用PlayWriter循环访问JSON对象

如果主对象中已存在属性,则不应在Jolt中引用次对象

使用 JSON 和相对日期设置日历视图中 SharePoint 列表项的背景 colored颜色 格式

使用 jq 和脚本 bash 映射两个 json

数据清理设计不良的 JSON 数据 - 需要有关最佳策略的建议

用于遮蔽卡的 Jolt 规格

如何在 Flutter 中遍历嵌套的动态 JSON 文件

如何在 Dart 中与多个 map (字典)相交

传统编程语言等价于动态 SQL

jq搜索特定字符串并输出对应的父值

字典和对象的模型创建问题

将错误消息作为 JSON 对象发送

jQuery fullcalendar 发送自定义参数并使用 JSON 刷新日历

如何将 Swift 对象转换为字典

jQuery循环.each()JSON键/值不起作用

我可以使用空字符串作为对象标识符吗?

有 Json 标签但未导出

无法将空值放入 JSON 对象

Javascript:如何判断 AJAX 响应是否为 JSON

使用 JSONArray 和 JSONObject 进行 Foreach