我可以使用JsonCpp来部分验证JSON输入吗
Can I use JsonCpp to partially-validate JSON input?
我使用JsonCpp在C++中解析JSON。
例如
Json::Reader r;
std::stringstream ss;
ss << "{"name": "sample"}";
Json::Value v;
assert(r.parse(ss, v)); // OK
assert(v["name"] == "sample"); // OK
但我的实际输入是JSON消息的整个流,这些消息可能以任何大小的块到达;我所能做的就是让JsonCpp尝试逐个字符地解析我的输入,在发现它们时吃掉完整的JSON消息:
Json::Reader r;
std::string input = "{"name": "sample"}{"name": "aardvark"}";
for (size_t cursor = 0; cursor < input.size(); cursor++) {
std::stringstream ss;
ss << input.substr(0, cursor);
Json::Value v;
if (r.parse(ss, v)) {
std::cout << v["name"] << " ";
input.erase(0, cursor);
}
} // Output: sample aardvark
这已经有点令人讨厌了,但情况确实变得更糟了。当输入的一部分丢失时(无论出于何种原因),我还需要能够重新同步。
现在它不一定是无损的,但我想防止以下输入可能永远破坏解析器:
{"name": "samp{"name": "aardvark"}
将此输入传递给JsonCpp将失败,但当我们在缓冲区中接收到更多字符时,这个问题不会消失;则第二CCD_ 1直接在其之前的CCD_;缓冲区永远无法完成以呈现有效的JSON。
然而,如果我被告知片段在第二个n
字符时肯定会变为无效,那么我可以将缓冲区中的所有内容都丢弃到该点,然后简单地等待下一个{
来考虑新对象的开始,作为尽最大努力的重新同步。
那么,有没有一种方法可以让JsonCpp告诉我,JSON的一个不完整片段是否已经保证了完整的"对象"在语法上是无效的?
即:
{"name": "sample"} Valid (Json::Reader::parse == true)
{"name": "sam Incomplete (Json::Reader::parse == false)
{"name": "sam"LOL Invalid (Json::Reader::parse == false)
我想区分这两种失败状态。
我可以使用JsonCpp来实现这一点吗?还是我必须通过构建一个状态机来编写我自己的JSON"部分验证器",该状态机在输入字符串的每一步都考虑哪些字符是"有效的"?我宁愿不重新发明轮子。。。
这当然取决于你是否真的控制了数据包(以及生产者)。如果你这样做,最简单的方法是在标题中指示边界:
+---+---+---+---+-----------------------
| 3 | 16|132|243|endofprevious"}{"name":...
+---+---+---+---+-----------------------
标题很简单:
- 3表示边界的数量
- 16、132和243表示每个边界的位置,它们对应于新对象(或列表)的左括号
然后是缓冲器本身。
在接收到这样的数据包时,可以解析以下条目:
previous + current[0:16]
current[16:132]
current[132:243]
并且current[243:]
被保存用于下一个数据包(尽管您总是可以尝试解析它,以防它完成)。
通过这种方式,数据包是自动同步的,并且没有模糊检测,以及它所带来的所有故障情况。
注意,在分组中可能存在0
边界。它只是意味着一个对象足够大,可以跨越几个数据包,您只需要暂时积累。
我建议让数字表示"固定"(例如,每个4个字节),并确定字节顺序(您的机器的字节顺序),以便轻松地将它们转换为二进制。我认为开销相当小(假设name
0已经是11个字节,则每个条目4个字节+4个字节)。
逐字符遍历缓冲区并手动检查:
- 字母字符的存在
- 字符串之外(不过要注意
"
可以用转义)
- 不属于
null
、true
或false
- 不是
e
或E
,它看起来像是带指数的数字文字
- 字符串之外(不过要注意
- 在字符串之外但紧接在
"
之后的数字的存在
并不是包罗万象,但我认为它涵盖了足够多的情况,可以在消息截断点或相当接近消息截断点时相当可靠地中断解析。
它正确地接受:
{"name": "samL
{"name": "sam0
{"name": "sam", 0
{"name": true
作为有效的JSON片段,但捕获:
{"name": "sam"L
{"name": "sam"0
{"name": "sam"true
被认为是不可接受的。
因此,以下输入都将导致完整的尾随对象被成功解析:
1. {"name": "samp{"name": "aardvark"}
// ^ ^
// A B - B is point of failure.
// Stripping leading `{` and scanning for the first
// free `{` gets us to A. (*)
{"name": "aardvark"}
2. {"name": "samp{"0": "abc"}
// ^ ^
// A B - B is point of failure.
// Stripping and scanning gets us to A.
{"0": "abc"}
3. {"name":{ "samp{"0": "abc"}
// ^ ^ ^
// A B C - C is point of failure.
// Stripping and scanning gets us to A.
{ "samp{"0": "abc"}
// ^ ^
// B C - C is still point of failure.
// Stripping and scanning gets us to B.
{"0": "abc"}
我的实现通过了一些相当彻底的单元测试。尽管如此,我想知道这种方法本身是否可以在不增加复杂性的情况下得到改进。
*我实际上在每条消息前都加了一个sentinel字符串,这使得"剥离和扫描"部分更加可靠,而不是寻找一个前导"{"
只需看看expat或其他流式xml解析器。如果不是的话,jsoncpp的逻辑应该是相似的。(如果需要,请该库的开发人员改进流读取。)
换句话说,从我的角度来看:
-
如果你的一些网络(而不是JSON)数据包丢失了——这不是JSON解析器的问题,只需使用更可靠的协议或发明自己的协议。然后通过它传输JSON。
-
如果JSON解析器报告错误,并且此错误发生在最后一个解析的令牌上(流中没有更多数据,但预期会有),则累积数据并重试(此任务应由库本身完成)。
但有时它可能不会报告错误。例如,当您转接123456时,只收到123。但这与您的情况不匹配,因为您不在单个JSON数据包中传输原始数据。
-
如果流包含有效数据包,然后是半接收数据包,则应为每个有效数据包调用一些回调。
-
如果JSON解析器报告错误,并且它确实是无效的JSON,则应关闭流,并在必要时再次打开。
- 在c++中使用nlohmann从类到json的转换
- 正在尝试了解输入验证循环
- C++json插入数组
- 如何在C++中检查2D数组中负值的输入验证
- nlohmann-json将一个数组插入到另一个数组中
- 如何使用curlpp通过POST方法上传文件和json数据
- LibGit2 SSH身份验证失败
- JSON转换为nlohmann JSON-lib中的结构数组
- Vulkan验证层不断在VkQueuePresentKHR()上抛出图像布局错误
- NLOHMANN 的 JSON 库将数组转换为结构向量
- constexpr上下文中std::initializer_list的验证
- 在nlohmann json中,如何将嵌套对象的数组转换为嵌套结构的向量
- 如何在 nlohmann 的 json 库中获取数组长度?
- 正在验证c++中用户的整数输入
- FlatC是否验证了给定JSON的FlatBuffer模式所需的字段
- JSON 请求在名称验证检查期间忽略''
- 如何在Objective-C中验证JSON模式
- 无效输入会导致多语言 JSON 消息传递系统中的身份验证绕过
- 我可以使用JsonCpp来部分验证JSON输入吗
- 将 valijson 与 Nlohmann 的 JSON for Modern C++ 结合使用,以验证具有子模式的模式