【问题标题】:C++: parsing JSON string, having keys not enclosed into double quotesC ++:解析JSON字符串,键不包含在双引号中
【发布时间】:2016-10-08 02:33:47
【问题描述】:

我已经成功使用 Casablanca Json C++ 库 (cpprest) 有一段时间了。它的解析器 (web::json::value::parse(<json_string>)) 可以完美地处理 valid JSON 字符串。说这将被正确解析:

{
  "key1": [["1", 0.4], ["0", 0.6]],
  "key2": true,
  "key3": 1,
  "key4": [{"key41": 1}, {"key42": [1,2,3]}]
}    

现在,我面临解析 JSON 对象的必要性,其键没有用双引号括起来:

{
  key1: [[1, 0.4], [0, 0.6]],
  key2: true,
  key3: 1,
  key4: [{key41: 1}, {key42: [1,2,3]}]
}

有没有一种很好的方法来正确解析这个然后序列化成一个有效的 JSON,这样 Casablanca 就可以正确解析得到的有效 JSON?

Hjson 似乎可以用于此目的,但它不提供 C++ 所需的库。他们提到了 C 的 jzon 库——我试过了:它只有单向解析(没有序列化),甚至解析也不能正常工作(甚至无法解析有效的 JSON)

【问题讨论】:

  • 所以您要的是能够做到这一点的第 3 方资源,但您知道这类问题是题外话吗?
  • @πάνταῥεῖ 他是否要求将“”转换为无?这就是我的理解。好的,我要开始回答了
  • 您是否有机会控制产生这种情况的任何东西?这样你就可以让它生成有效的 JSON?
  • @πάνταῥεῖ 不,我没有说过这个..任何好的 c++ 预处理解析解决方案也可以正常工作。但如果有人知道第三方库,为什么不呢?
  • @littlepootis 不,如果我愿意的话——我一开始就不会遇到这个问题

标签: c++ json casablanca


【解决方案1】:

这可能不是最快的方法,但如果以最少的代码行数来衡量,它会非常高。

您拥有的是一个类似 javascript 的对象。让我们将它插入一个 javascript 引擎并使用它来输出正确的 JSON。我将使用 Qt 的 QJSEngine,因为我对它非常熟悉:

constexpr char const* str = R"({
    key1: [[1, 0.4], [0, 0.6]],
    key2: true,
    key3: 1,
    key4: [{key41: 1}, {key42: [1,2,3]}]
})";

QJSEngine e;

QString script = QString("JSON.stringify(%0)").arg(str);

那么你可以评估它:

e.evaluate(script).toString().toStdString()

产量

{"key1":[[1,0.4],[0,0.6]],"key2":true,"key3":1,"key4":[{"key41":1},{"key42":[1,2,3]}]}

【讨论】:

  • 谢谢!嗯,这是一个有趣的方法。我很快就会试一试
【解决方案2】:

这种粗略的方法会奏效。

(未经测试的代码)

我们有三种状态,NEUTRAL、ONSTRING 和 ONALNUM。

我们从 NEUTRAL 开始。如果我们点击'"',我们进入 ONSTRING。如果我们点击 alpha,我们进入 ONALNUM。如果我们进入或离开 alnum,我们发出一个引号。我们也发出读取的字符。 如果我们在 ONALNUM 中,当我们点击 non-alnum 时我们会退出它,并且进入 NEUTRAL,除非我们点击引号,当它是一个解析错误时。如果我们在 ONSTRING 中,我们会应用 JSON 字符串转义规则,我不知道。

 #define NEUTRAL 0
 #define ONSTRING 1
 #define ONALNUM 2

 int state = NEUTRAL;
 char *inptr = str;
 char ch;

 while( (ch = *inptr++))
 {
    if(state == NEUTRAL)
    {
       if( isalpha(ch) )
       {
           emit('\"');
           state = ONALNUM;
       }
       else if(ch = '\"')
          state = ONSTRING;
       emit(ch);
    }
    else if(state == ONSTRING)
    {
        /* JSON string escape rules here */
        if(ch == '\"')
          state = NEUTRAL;
        emit(ch);
    }
    else if(state == ONALNUM)
    {
       emit(ch);
       if(!isalnum(ch))
       {
          state = NEUTRAL;
          emit('\"');
       }
    }
 }

【讨论】:

    【解决方案3】:

    问题

    从您的问题上下文看来,您希望删除“”,以便您可以使用正确的 JSON 格式。

    对于 JSON 解析部分,您应该使用库。我很快就会在这里发布一个例子。

    解决方案

    为此,我们将使用来自<algorithm> 库的std::replace 函数;尽管我们可以自己实现这一点,但最好使用标准库,因为它们的创建者努力优化这些功能以发挥最大的功能。因此,让我们从问题中获取您提供给我们的代码,并使其适合 JSON。

    #include <algorithm>
    #include <string>
    #include <iostream>
    
    using std::string;
    using std::cout;
    using std::endl;
    
    void convert_char(string &s,char from_conv, char to_conv) {
      std::replace( s.begin(), s.end(), from_conv, to_conv); // replace all 'x' to 'y'
    }
    
    int main()
    {
        string str =  "{ \n \
        \"key1\": [[\"1\", 0.4], [\"0\", 0.6]], \n \
        \"key2\": true, \n \
        \"key3\": 1,  \n \
        \"key4\": [{\"key41\": 1}, {\"key42\": [1,2,3]}] \n }";;
        convert_char(str,'\"',(char)0);
        cout << str << endl;
    }
    

    您可以在这里看到我们有一个名为convert_char 的函数,它将某个字符转换为另一个字符。所以基本上正如你提出的问题,我们删除了双引号,tada,它的格式类似于 JSON!看看here 的演示。

    JSON解析器解决方案

    显然,在这里您将使用一个库来为您执行此操作。我要给大家介绍sciter!基本上,有了sciter,你所要做的就是:

    #include <algorithm>
    #include <string>
    #include <iostream>
    #include <sciter>
    
    using std::string;
    using std::cout;
    using std::endl;
    
    int main()
    {
        string str =  "{ \n \
        \"key1\": [[\"1\", 0.4], [\"0\", 0.6]], \n \
        \"key2\": true, \n \
        \"key3\": 1,  \n \
        \"key4\": [{\"key41\": 1}, {\"key42\": [1,2,3]}] \n }";;
        sciter::value str_conv = sciter::value::from_string( str, CVT_JSON_LITERAL );
        cout << str_conv << endl;
    }
    

    现在根据这段代码,JSON格式的代码在str_conv!下面的参考资料部分提供了指导您完成此操作的参考资料。

    参考资料:

    sciter

    cpprefrence std::replace

    string::replace

    cpprefrence std::refrence_if

    词汇表

    std::replace:

    原型:

    template <class ForwardIterator, class T>
      void replace (ForwardIterator first, ForwardIterator last,
                    const T& old_value, const T& new_value); //source cpprefrence
    

    参考:cpprefrence

    &lt;algorithm&gt;:

    算法库中包含许多主题。你猜对了,它是一个算法库。

    标题&lt;algorithm&gt; 定义了一组专门设计用于元素范围的函数。

    范围是可以通过迭代器或指针访问的任何对象序列,例如数组或某些 STL 容器的实例。但请注意,algorithms 通过 iterators 直接对值进行操作,不会以任何方式影响任何可能容器的结构(它不会影响容器的大小或存储分配)。

    算法库为各种目的(例如搜索、排序、计数、操作)定义了对元素范围进行操作的函数。

    参考文献:

    cplusplus

    cpprefrence

    【讨论】:

    • 反之。 JSON 坚持对标识符和字符串进行引号。
    • 即使这是 OP 想要的,正确的方法是使用擦除删除习语,而不是让字符串充满'\0' 字符。
    • @MalcolmMcLean 此问题已得到修复。看看新的答案;)
    • 谢谢,但我认为这没有帮助。对于第二部分(解析) - 我没有任何问题 - 我提到我广泛使用 Casablanca 库来处理 JSON - 没有任何问题。第一部分(替换“”)也不起作用 - 因为 JSON 对中的 VALUES 周围仍然有双引号,而不仅仅是键
    • @amanuel2 replace 正是这样做的——替换元素。您将 (char)0 - C 空终止符放置在 " 曾经的每个位置。尝试将str_conv.c_str() 传递给cout,看看发生了什么。
    猜你喜欢
    • 2020-03-17
    • 2016-04-15
    • 1970-01-01
    • 1970-01-01
    • 2019-01-22
    • 2019-10-22
    • 2015-09-09
    • 2020-01-03
    • 1970-01-01
    相关资源
    最近更新 更多