2012-02-14 32 views
1

我使用JsonCpp來解析C++中的JSON。我可以使用JsonCpp部分驗證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會失敗,但問題我們收到更多的字符進入緩衝區不會消失;那第二個name在它之前的"之後直接是無效的;該緩衝區永遠無法完成呈現有效的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「部分驗證器」,該狀態機在輸入字符串的每一步中考慮哪些字符是「有效的」?我寧願不重新發明輪子...

回答

2

通過緩衝字符逐字符和迭代手動檢查:

  • 的字母字符的存在
    • 字符串(小心的是"可以與\轉義,雖然)的外
    • 不是nulltruefalse
    • 不是部分0或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"} 

我實現通過一些相當徹底的單元測試。不過,我不知道是否該方法本身不能夠在複雜的爆炸得到改善。


*而不是尋找一個領先"{",我居然有前置到每一個消息,這使得更可靠的「剝離和掃描」部分定點字符串。

2

這當然取決於你實際控制數據包(因此製片人),還是不行。如果你這樣做,最簡單的方法就是表明在頭的界限:

+---+---+---+---+----------------------- 
| 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":""}已經是11個字節,每個條目4字節+ 4字節)。

+0

生產者只知道一個完整的JSON對象。分割完全取決於能夠通過網絡傳輸的內容。 – 2012-02-14 10:06:55

+0

(我不希望我們的協議有一個消息長度字段,但實際上,我們_do_必須在每JSON消息,看起來像'鴿子開始定點:JSON:',所以我們可以建立它變成唉也許。在下一個版本中:D) – 2012-02-14 12:00:45

+0

我能夠最終調整格式,生成包含長度標題的協議的替代版本;然而,爲了向後兼容,我仍然需要爲現有格式解決這個問題。 – 2012-02-16 17:04:47

0

只要看看外籍人士或其他流XML解析器。如果沒有,jsoncpp的邏輯應該是相似的。 (如果需要,要求開發商這個庫,以提高流閱讀。)

換句話說,從我的觀點:

  1. 如果一些網絡(未JSON)數據包丟失它不是JSON解析器的問題,只是使用更可靠的協議或發明自己的。只有然後將JSON轉移到它上面。

  2. 如果JSON解析器報告錯誤,並且在最後解析的標記上發生了此錯誤(沒有更多數據在數據流中但期望) - 累積數據並重試(此任務應由庫本身完成)。

    雖然有時它可能不會報告錯誤。例如,當您傳送123456並且只收到123時。但是這不符合您的情況,因爲您不會在單個JSON數據包中傳輸原始數據。

  3. 如果流包含有效的數據包,然後半收到的數據包,一些回調應該呼籲每個有效的數據包。

  4. 如果JSON解析器報告錯誤,這真的無效JSON,流應重新如有必要,關閉和打開。

+0

我也在尋找流式json解析器,並嘗試jsoncpp。現在,我將包含數據包長度的uint32放在我的json數據包中,我不喜歡這個解決方案,因爲當我混合這樣的二進制數據json時,它對測試稍微不方便。 – Sergey 2012-12-27 09:45:35

相關問題