jsoncons 是一個 C++ 純標頭庫,用於建立 JSON 和類似 JSON 的資料格式,例如 CBOR。對於每種支援的資料格式,它使您能夠透過多種方式處理資料:
作為一個類似變體的、分配器感知的資料結構,basic_json
作為實作 json_type_traits 的強型別 C++ 資料結構
透過遊標級存取解析事件流,有點類似 XML 世界中的 StAX 拉解析和推送序列化。
與其他 JSON 庫相比,jsoncons 被設計用於處理非常大的 JSON 文字。其核心是 SAX 風格的解析器和序列化器。它支援以類似變體的結構讀取記憶體中的整個 JSON 文字。但它還支援使用 StAX 風格的拉式解析和推式序列化來高效存取底層資料。它支援使用 json_type_traits 專業化提供的有關使用者類型的信息,增量解析為使用者的首選形式。
jsoncons 資料模型支援熟悉的 JSON 類型 - 空值、布林值、數字、字串、陣列、物件 - 以及位元組字串。此外,jsoncons 支援日期時間、紀元時間、大整數、大小數、大浮點數和二元編碼的語義標記。這使得它能夠在解析類似 JSON 的資料格式(例如具有這些類型語義的 CBOR)時保留這些類型語義。
jsoncons 根據 Boost 軟體授權分發。
jsoncons 是免費的,但歡迎支持以維持其發展。如果您發現這個圖書館有幫助,請考慮一次性捐贈或成為❤️贊助商。
隨著jsoncons
庫的發展,名稱有時會改變。為了簡化過渡,jsoncons 棄用了舊名稱,但繼續支持其中的許多名稱。可以透過定義巨集JSONCONS_NO_DEPRECATED
來抑制已棄用的名稱,建議新程式碼這樣做。
“Apache Kvrocks 始終利用 jsoncons 為用戶提供對 JSON 資料結構的支援。我們發現 jsoncons 的開發體驗非常出色!”
「我一直在用我的母語R 來使用你們的庫,並創建了一個R 包,以便(a) 對JSON 字符串或R 對象進行JMESpath 和JSONpath 查詢,以及(b) 其他R 開發人員鏈接到您的庫」。
“我使用您的庫作為外部接口來傳遞數據,以及使用從 csv 到 json 的轉換,這對於轉換數據以在 javascript 中使用非常有幫助”
“已驗證,根據我對 JSON 和 CBOR 的需求,它運行良好”
“這個庫的 JSONPath 功能,非常棒”
“我們相當廣泛地使用 JMESPath 實現”
“我們喜歡您的 JSON 模式驗證器。我們在 ER/Studio 數據建模工具中使用它來解析 JSON 模式文件,以便我們可以從中創建實體關係模型。”
“選擇的序列化庫具有漂亮的映射和易用性”
“真的很好” “很棒的項目” “非常堅固且非常可靠” “我的團隊喜歡它” “你的倉庫很棒!!!!!”
開始使用 AWS 開發工具包使用 HealthImaging 影像集和影像幀
RubyGems.org rjsoncons Coppelia Robotics CSPro 許可證
您可以使用 vcpkg 平台庫管理員來安裝 jsoncons 套件。
或者,下載最新版本並解壓縮 zip 檔案。將目錄include/jsoncons
複製到您的include
目錄。如果您想使用擴展,請同時複製include/jsoncons_ext
。
或者,在 main.c 上下載最新的程式碼。
該函式庫需要支援 C++11 的 C++ 編譯器。此外,該庫定義了jsoncons::endian
、 jsoncons::basic_string_view
、 jsoncons::optional
和jsoncons::span
,如果偵測到它們,它們將被類型定義為其標準庫等效項。否則,它們將被類型定義為內部的、C++11 相容的實作。
該庫使用異常,在某些情況下使用 std::error_code 來報告錯誤。除了jsoncons::assertion_error
之外,所有 jsoncons 異常類別都實作 jsoncons::json_error 介面。如果停用異常或定義了編譯時巨集JSONCONS_NO_EXCEPTIONS
,則拋出將變成對std::terminate
呼叫。
json_benchmarks 提供了一些有關jsoncons
與其他json
庫比較的測量結果。
JSONTestSuite 和 JSON_checker 測試套件
文字和整數的效能基準
文字和雙打的性能基準
JSONPath Comparison 顯示 jsoncons JsonPath 與其他實作的比較
處理 JSON 數據
使用 CBOR 數據
對於下面的範例,您需要包含一些頭檔並初始化一串 JSON 資料:
# include < jsoncons/json.hpp >
# include < jsoncons_ext/jsonpath/jsonpath.hpp >
# include < iostream >
using namespace jsoncons ; // for convenience
std::string data = R"(
{
"application": "hiking",
"reputons": [
{
"rater": "HikingAsylum",
"assertion": "advanced",
"rated": "Marilyn C",
"rating": 0.90,
"generated": 1514862245
}
]
}
)" ;
jsoncons 允許您以多種方式處理資料:
basic_json 作為一種類似變體的資料結構
作為實作 json_type_traits 的強型別 C++ 資料結構
透過遊標級存取解析事件流
int main ()
{
// Parse the string of data into a json value
json j = json::parse (data);
// Does object member reputons exist?
std::cout << " (1) " << std::boolalpha << j. contains ( " reputons " ) << " nn " ;
// Get a reference to reputons array
const json& v = j[ " reputons " ];
// Iterate over reputons array
std::cout << " (2) n " ;
for ( const auto & item : v. array_range ())
{
// Access rated as string and rating as double
std::cout << item[ " rated " ]. as <std::string>() << " , " << item[ " rating " ]. as < double >() << " n " ;
}
std::cout << " n " ;
// Select all "rated" with JSONPath
std::cout << " (3) n " ;
json result = jsonpath::json_query (j, " $..rated " );
std::cout << pretty_print (result) << " nn " ;
// Serialize back to JSON
std::cout << " (4) n " << pretty_print (j) << " nn " ;
}
輸出:
(1) true
(2)
Marilyn C, 0.9
(3)
[
"Marilyn C"
]
(4)
{
"application": "hiking",
"reputons": [
{
"assertion": "advanced",
"generated": 1514862245,
"rated": "Marilyn C",
"rater": "HikingAsylum",
"rating": 0.9
}
]
}
jsoncons 支援將 JSON 文字轉換為 C++ 資料結構。函數decode_json 和encode_json 將字串或JSON 資料流轉換為C++ 資料結構並傳回。對定義了 json_type_traits 的所有 C++ 類別進行解碼和編碼工作。 jsoncons 已經支援標準庫中的許多類型,如果您在jsoncons
命名空間中專門化json_type_traits
,您自己的類型也將受到支援。
namespace ns {
enum class hiking_experience {beginner,intermediate,advanced};
class hiking_reputon
{
std::string rater_;
hiking_experience assertion_;
std::string rated_;
double rating_;
std::optional<std::chrono::seconds> generated_; // assumes C++17, if not use jsoncons::optional
std::optional<std::chrono::seconds> expires_;
public:
hiking_reputon ( const std::string& rater,
hiking_experience assertion,
const std::string& rated,
double rating,
const std::optional<std::chrono::seconds>& generated =
std::optional<std::chrono::seconds>(),
const std::optional<std::chrono::seconds>& expires =
std::optional<std::chrono::seconds>())
: rater_(rater), assertion_(assertion), rated_(rated), rating_(rating),
generated_ (generated), expires_(expires)
{
}
const std::string& rater () const { return rater_;}
hiking_experience assertion () const { return assertion_;}
const std::string& rated () const { return rated_;}
double rating () const { return rating_;}
std::optional<std::chrono::seconds> generated () const { return generated_;}
std::optional<std::chrono::seconds> expires () const { return expires_;}
friend bool operator ==( const hiking_reputon& lhs, const hiking_reputon& rhs)
{
return lhs. rater_ == rhs. rater_ && lhs. assertion_ == rhs. assertion_ &&
lhs. rated_ == rhs. rated_ && lhs. rating_ == rhs. rating_ &&
lhs. confidence_ == rhs. confidence_ && lhs. expires_ == rhs. expires_ ;
}
friend bool operator !=( const hiking_reputon& lhs, const hiking_reputon& rhs)
{
return !(lhs == rhs);
};
};
class hiking_reputation
{
std::string application_;
std::vector<hiking_reputon> reputons_;
public:
hiking_reputation ( const std::string& application,
const std::vector<hiking_reputon>& reputons)
: application_(application),
reputons_ (reputons)
{}
const std::string& application () const { return application_;}
const std::vector<hiking_reputon>& reputons () const { return reputons_;}
};
} // namespace ns
// Declare the traits. Specify which data members need to be serialized.
JSONCONS_ENUM_TRAITS (ns::hiking_experience, beginner, intermediate, advanced)
// First four members listed are mandatory, generated and expires are optional
JSONCONS_N_CTOR_GETTER_TRAITS(ns::hiking_reputon, 4 , rater, assertion, rated, rating,
generated, expires)
// All members are mandatory
JSONCONS_ALL_CTOR_GETTER_TRAITS(ns::hiking_reputation, application, reputons)
int main()
{
// Decode the string of data into a c++ structure
ns::hiking_reputation v = decode_json<ns::hiking_reputation>(data);
// Iterate over reputons array value
std::cout << " (1) n " ;
for ( const auto & item : v. reputons ())
{
std::cout << item. rated () << " , " << item. rating ();
if (item. generated ())
{
std::cout << " , " << (*item. generated ()). count ();
}
std::cout << " n " ;
}
// Encode the c++ structure into a string
std::string s;
encode_json (v, s, indenting::indent);
std::cout << " (2) n " ;
std::cout << s << " n " ;
}
輸出:
(1)
Marilyn C, 0.9, 1514862245
(2)
{
"application": "hiking",
"reputons": [
{
"assertion": "advanced",
"generated": 1514862245,
"rated": "Marilyn C",
"rater": "HikingAsylum",
"rating": 0.9
}
]
}
此範例使用便捷宏JSONCONS_ENUM_TRAITS
、 JSONCONS_N_CTOR_GETTER_TRAITS
和JSONCONS_ALL_CTOR_GETTER_TRAITS
來專門針對枚舉類型ns::hiking_experience
、類別ns::hiking_reputon
JSONCONS_ENUM_TRAITS
帶有一些非強製成員)和ns::hiking_reputation
宏類成員(CITA_UITS鍛煉)。枚舉標識符產生程式碼,巨集JSONCONS_N_CTOR_GETTER_TRAITS
和JSONCONS_ALL_CTOR_GETTER_TRAITS
從 get 函式和建構函式產生程式碼。這些巨集聲明必須放置在任何命名空間區塊之外。
請參閱範例以了解專門化json_type_traits
的其他方法。
典型的拉式解析應用程式將重複處理current()
事件並呼叫next()
前進到下一個事件,直到done()
傳回true
。
int main ()
{
json_string_cursor cursor (data);
for (; !cursor. done (); cursor. next ())
{
const auto & event = cursor. current ();
switch (event. event_type ())
{
case staj_event_type::begin_array:
std::cout << event. event_type () << " " << " n " ;
break ;
case staj_event_type::end_array:
std::cout << event. event_type () << " " << " n " ;
break ;
case staj_event_type::begin_object:
std::cout << event. event_type () << " " << " n " ;
break ;
case staj_event_type::end_object:
std::cout << event. event_type () << " " << " n " ;
break ;
case staj_event_type::key:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " n " ;
break ;
case staj_event_type::string_value:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " n " ;
break ;
case staj_event_type::null_value:
std::cout << event. event_type () << " n " ;
break ;
case staj_event_type::bool_value:
std::cout << event. event_type () << " : " << std::boolalpha << event. get < bool >() << " n " ;
break ;
case staj_event_type::int64_value:
std::cout << event. event_type () << " : " << event. get < int64_t >() << " n " ;
break ;
case staj_event_type::uint64_value:
std::cout << event. event_type () << " : " << event. get < uint64_t >() << " n " ;
break ;
case staj_event_type::double_value:
std::cout << event. event_type () << " : " << event. get < double >() << " n " ;
break ;
default :
std::cout << " Unhandled event type: " << event. event_type () << " " << " n " ;
break ;
}
}
}
輸出:
begin_object
key: application
string_value: hiking
key: reputons
begin_array
begin_object
key: rater
string_value: HikingAsylum
key: assertion
string_value: advanced
key: rated
string_value: Marilyn C
key: rating
double_value: 0.9
key: generated
uint64_value: 1514862245
end_object
end_array
end_object
您可以使用管道語法將過濾器套用至遊標(例如, cursor | filter1 | filter2 | ...
)
int main ()
{
std::string name;
auto filter = [&]( const staj_event& ev, const ser_context&) -> bool
{
if (ev. event_type () == staj_event_type::key)
{
name = ev. get <std::string>();
return false ;
}
if (name == " rated " )
{
name. clear ();
return true ;
}
return false ;
};
json_string_cursor cursor (data);
auto filtered_c = cursor | filter;
for (; !filtered_c. done (); filtered_c. next ())
{
const auto & event = filtered_c. current ();
switch (event. event_type ())
{
case staj_event_type::string_value:
// Or std::string_view, if C++17
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " n " ;
break ;
default :
std::cout << " Unhandled event type n " ;
break ;
}
}
}
輸出:
Marilyn C
對於下面的範例,您需要包含一些頭檔並初始化 CBOR 資料的緩衝區:
# include < iomanip >
# include < iostream >
# include < jsoncons/json.hpp >
# include < jsoncons_ext/cbor/cbor.hpp >
# include < jsoncons_ext/jsonpath/jsonpath.hpp >
using namespace jsoncons ; // for convenience
const std::vector< uint8_t > data = {
0x9f , // Start indefinte length array
0x83 , // Array of length 3
0x63 , // String value of length 3
0x66 , 0x6f , 0x6f , // "foo"
0x44 , // Byte string value of length 4
0x50 , 0x75 , 0x73 , 0x73 , // 'P''u''s''s'
0xc5 , // Tag 5 (bigfloat)
0x82 , // Array of length 2
0x20 , // -1
0x03 , // 3
0x83 , // Another array of length 3
0x63 , // String value of length 3
0x62 , 0x61 , 0x72 , // "bar"
0xd6 , // Expected conversion to base64
0x44 , // Byte string value of length 4
0x50 , 0x75 , 0x73 , 0x73 , // 'P''u''s''s'
0xc4 , // Tag 4 (decimal fraction)
0x82 , // Array of length 2
0x38 , // Negative integer of length 1
0x1c , // -29
0xc2 , // Tag 2 (positive bignum)
0x4d , // Byte string value of length 13
0x01 , 0x8e , 0xe9 , 0x0f , 0xf6 , 0xc3 , 0x73 , 0xe0 , 0xee , 0x4e , 0x3f , 0x0a , 0xd2 ,
0xff // "break"
};
jsoncons 允許您像 JSON 資料一樣使用 CBOR 資料:
basic_json 作為一種類似變體的資料結構
作為實作 json_type_traits 的強型別 C++ 資料結構
透過遊標級存取解析事件流
int main ()
{
// Parse the CBOR data into a json value
json j = cbor::decode_cbor<json>(data);
// Pretty print
std::cout << " (1) n " << pretty_print (j) << " nn " ;
// Iterate over rows
std::cout << " (2) n " ;
for ( const auto & row : j. array_range ())
{
std::cout << row[ 1 ]. as <jsoncons::byte_string>() << " ( " << row[ 1 ]. tag () << " ) n " ;
}
std::cout << " n " ;
// Select the third column with JSONPath
std::cout << " (3) n " ;
json result = jsonpath::json_query (j, " $[*][2] " );
std::cout << pretty_print (result) << " nn " ;
// Serialize back to CBOR
std::vector< uint8_t > buffer;
cbor::encode_cbor (j, buffer);
std::cout << " (4) n " << byte_string_view (buffer) << " nn " ;
}
輸出:
(1)
[
["foo", "UHVzcw", "0x3p-1"],
["bar", "UHVzcw==", "1.23456789012345678901234567890"]
]
(2)
50,75,73,73 (n/a)
50,75,73,73 (base64)
(3)
[
"0x3p-1",
"1.23456789012345678901234567890"
]
(4)
82,83,63,66,6f,6f,44,50,75,73,73,c5,82,20,03,83,63,62,61,72,d6,44,50,75,73,73,c4,82,38,1c,c2,4d,01,8e,e9,0f,f6,c3,73,e0,ee,4e,3f,0a,d2
int main ()
{
// Parse the string of data into a std::vector<std::tuple<std::string,jsoncons::byte_string,std::string>> value
auto val = cbor::decode_cbor<std::vector<std::tuple<std::string,jsoncons::byte_string,std::string>>>(data);
std::cout << " (1) n " ;
for ( const auto & row : val)
{
std::cout << std::get< 0 >(row) << " , " << std::get< 1 >(row) << " , " << std::get< 2 >(row) << " n " ;
}
std::cout << " n " ;
// Serialize back to CBOR
std::vector< uint8_t > buffer;
cbor::encode_cbor (val, buffer);
std::cout << " (2) n " << byte_string_view (buffer) << " nn " ;
}
輸出:
(1)
foo, 50,75,73,73, 0x3p-1
bar, 50,75,73,73, 1.23456789012345678901234567890
(2)
82,9f,63,66,6f,6f,44,50,75,73,73,66,30,78,33,70,2d,31,ff,9f,63,62,61,72,44,50,75,73,73,78,1f,31,2e,32,33,34,35,36,37,38,39,30,31,32,33,34,35,36,37,38,39,30,31,32,33,34,35,36,37,38,39,30,ff
請注意,當將 bigfloat 和小數部分解碼為std::string
時,我們會遺失變數(例如使用標籤儲存的資料結構)的語義訊息,因此序列化回 CBOR 會產生文字字串。
典型的拉式解析應用程式將重複處理current()
事件並呼叫next()
前進到下一個事件,直到done()
傳回true
。
int main ()
{
cbor::cbor_bytes_cursor cursor (data);
for (; !cursor. done (); cursor. next ())
{
const auto & event = cursor. current ();
switch (event. event_type ())
{
case staj_event_type::begin_array:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::end_array:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::begin_object:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::end_object:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::key:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::string_value:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::byte_string_value:
std::cout << event. event_type () << " : " << event. get <jsoncons::span< const uint8_t >>() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::null_value:
std::cout << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::bool_value:
std::cout << event. event_type () << " : " << std::boolalpha << event. get < bool >() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::int64_value:
std::cout << event. event_type () << " : " << event. get < int64_t >() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::uint64_value:
std::cout << event. event_type () << " : " << event. get < uint64_t >() << " " << " ( " << event. tag () << " ) n " ;
break ;
case staj_event_type::half_value:
case staj_event_type::double_value:
std::cout << event. event_type () << " : " << event. get < double >() << " " << " ( " << event. tag () << " ) n " ;
break ;
default :
std::cout << " Unhandled event type " << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
}
}
}
輸出:
begin_array (n/a)
begin_array (n/a)
string_value: foo (n/a)
byte_string_value: 50,75,73,73 (n/a)
string_value: 0x3p-1 (bigfloat)
end_array (n/a)
begin_array (n/a)
string_value: bar (n/a)
byte_string_value: 50,75,73,73 (base64)
string_value: 1.23456789012345678901234567890 (bigdec)
end_array (n/a)
end_array (n/a)
您可以使用管道語法將過濾器套用至遊標,
int main ()
{
auto filter = [&]( const staj_event& ev, const ser_context&) -> bool
{
return (ev. tag () == semantic_tag::bigdec) || (ev. tag () == semantic_tag::bigfloat);
};
cbor::cbor_bytes_cursor cursor (data);
auto filtered_c = cursor | filter;
for (; !filtered_c. done (); filtered_c. next ())
{
const auto & event = filtered_c. current ();
switch (event. event_type ())
{
case staj_event_type::string_value:
// Or std::string_view, if supported
std::cout << event. event_type () << " : " << event. get <jsoncons::string_view>() << " " << " ( " << event. tag () << " ) n " ;
break ;
default :
std::cout << " Unhandled event type " << event. event_type () << " " << " ( " << event. tag () << " ) n " ;
break ;
}
}
}
輸出:
string_value: 0x3p-1 (bigfloat)
string_value: 1.23456789012345678901234567890 (bigdec)
jsoncons 需要一個至少支援 C++11 的編譯器。它在 Github Actions 和 Circleci 上進行了持續整合測試。為選定的 gcc 和 clang 版本啟用 UndefinedBehaviorSanitizer (UBSan) 診斷。自 v0.151.0 起,它與 Google OSS-fuzz 集成,覆蓋所有解析器和編碼器。
編譯器 | 版本 | 標準 | 建築學 | 作業系統 | 持續整合服務 |
---|---|---|---|---|---|
視覺工作室 | vs2019 | 預設 | x86、x64 | 視窗11 | GitHub 操作 |
vs2022 | 預設 | x86、x64 | 視窗11 | GitHub 操作 | |
Visual Studio - 鏗鏘聲 | vs2019 | 預設 | x86、x64 | 視窗11 | GitHub 操作 |
vs2022 | 預設 | x86、x64 | 視窗11 | GitHub 操作 | |
克++ | 6、7、8、9、10、11、12 | 預設 | x64 | 烏班圖 | 圈子 |
克++ | 12 | c++20 | x64 | 烏班圖 | GitHub 操作 |
鐺 | 3.9、4、5、6、7、8、9、10、11、12、13、14、15 | 預設 | x64 | 烏班圖 | 圈子 |
鐺 | 14 | c++20 | x64 | 烏班圖 | GitHub 操作 |
鏗鏘xcode | 11, 12, 13 | 預設 | x64 | 作業系統X 11 | GitHub 操作 |
鏗鏘xcode | 13, 14 | 預設 | x64 | 作業系統X 12 | GitHub 操作 |
CMake 是一個跨平台建置工具,可為您選擇的編譯器環境產生 makefile 和解決方案。在 Windows 上,您可以下載 Windows 安裝程式套件。在 Linux 上,它通常作為軟體包提供,例如,在 Ubuntu 上,
sudo apt-get install cmake
安裝 cmake 後,您可以從 jsoncons 目錄建置並執行單元測試,
在 Windows 上:
> mkdir build
> cd build
> cmake .. -DJSONCONS_BUILD_TESTS=On
> cmake --build .
> ctest -C Debug --output-on-failure
在 UNIX 上:
$ mkdir build
$ cd build
$ cmake .. -DJSONCONS_BUILD_TESTS=On
$ cmake --build .
$ ctest --output-on-failure
jsoncons 使用 PVS-Studio 靜態分析器,為開源專案免費提供。
非常感謝 comp.lang.c++ 社群在實作細節方面提供的幫助。
jsoncons 平台相關的二進位配置借鑒了優秀的 MIT 許可的tinycbor。
感謝 RapidJSON 的作者 Milo Yip,透過發布基準測試並聯繫該專案(以及其他專案)來分享結果,全面提高了 JSON 庫的品質。
用於列印浮點數的 Grisu3 演算法的 jsoncons 實作遵循 Florian Loitsch 的 MIT 許可的 grisu3_59_56 實現,並進行了少量修改。
巨集JSONCONS_ALL_MEMBER_TRAITS
遵循 Martin York 的 ThorsSerializer 所採用的方法
BSON Decimal128 與字串之間的 jsoncons 實作以及 ObjectId 與字串之間的實作基於 Apache 2 許可的 libbson。
特別感謝我們的貢獻者