json
dari literal JSONAda banyak sekali perpustakaan JSON di luar sana, dan masing-masing perpustakaan mungkin memiliki alasan tersendiri untuk keberadaannya. Kelas kami memiliki tujuan desain berikut:
Sintaks intuitif . Dalam bahasa seperti Python, JSON terasa seperti tipe data kelas satu. Kami menggunakan semua keajaiban operator C++ modern untuk mencapai perasaan yang sama dalam kode Anda. Lihat contoh di bawah ini dan Anda akan tahu apa yang saya maksud.
Integrasi sepele . Seluruh kode kami terdiri dari satu file header json.hpp
. Itu saja. Tidak ada perpustakaan, tidak ada subproyek, tidak ada ketergantungan, tidak ada sistem pembangunan yang rumit. Kelas ditulis dalam vanilla C++11. Secara keseluruhan, semuanya tidak memerlukan penyesuaian flag compiler atau pengaturan proyek Anda.
Pengujian serius . Kode kami telah diuji secara ketat dan mencakup 100% kode, termasuk semua perilaku luar biasa. Selanjutnya, kami memeriksa dengan Valgrind dan Clang Sanitizers bahwa tidak ada kebocoran memori. Google OSS-Fuzz juga menjalankan pengujian fuzz terhadap semua parser 24/7, dan sejauh ini secara efektif menjalankan miliaran pengujian. Untuk mempertahankan kualitas tinggi, proyek ini mengikuti praktik terbaik Inisiatif Infrastruktur Inti (CII).
Aspek lain tidak begitu penting bagi kami:
Efisiensi memori . Setiap objek JSON memiliki overhead satu pointer (ukuran maksimal gabungan) dan satu elemen enumerasi (1 byte). Generalisasi default menggunakan tipe data C++ berikut: std::string
untuk string, int64_t
, uint64_t
atau double
untuk angka, std::map
untuk objek, std::vector
untuk array, dan bool
untuk Boolean. Namun, Anda dapat membuat template kelas umum basic_json
sesuai kebutuhan Anda.
Kecepatan . Tentu saja ada perpustakaan JSON yang lebih cepat di luar sana. Namun, jika tujuan Anda adalah mempercepat pengembangan dengan menambahkan dukungan JSON dengan satu header, maka perpustakaan ini adalah pilihan yang tepat. Jika Anda tahu cara menggunakan std::vector
atau std::map
, Anda sudah siap.
Lihat pedoman kontribusi untuk informasi lebih lanjut.
Anda dapat mensponsori perpustakaan ini di GitHub Sponsors.
Terima kasih semuanya!
❓ Jika Anda memiliki pertanyaan , silakan periksa apakah pertanyaan tersebut sudah terjawab di FAQ atau bagian Tanya Jawab . Jika tidak, silakan ajukan pertanyaan baru di sana.
Jika Anda ingin mempelajari lebih lanjut tentang cara menggunakan perpustakaan, lihat README lainnya, lihat contoh kode , atau telusuri halaman bantuan .
? Jika Anda ingin memahami API dengan lebih baik, lihat Referensi API .
? Jika Anda menemukan bug , silakan periksa FAQ apakah itu masalah umum atau akibat keputusan desain. Silakan lihat juga daftar terbitan sebelum Anda membuat terbitan baru . Harap berikan informasi sebanyak mungkin untuk membantu kami memahami dan mereproduksi masalah Anda.
Ada juga dokumen untuk browser dokumentasi Dash, Velocity, dan Zeal yang berisi dokumentasi lengkap sebagai sumber offline.
Berikut adalah beberapa contoh untuk memberi Anda gambaran bagaimana menggunakan kelas.
Selain contoh di bawah, Anda mungkin ingin:
→ Periksa dokumentasinya
→ Telusuri file contoh mandiri
Setiap fungsi API (didokumentasikan dalam Dokumentasi API) memiliki file contoh mandiri yang sesuai. Misalnya, fungsi emplace()
memiliki file contoh emplace.cpp yang cocok.
Kelas json
menyediakan API untuk memanipulasi nilai JSON. Untuk membuat objek json
dengan membaca file JSON:
# include < fstream >
# include < nlohmann/json.hpp >
using json = nlohmann::json;
// ...
std::ifstream f ( " example.json " );
json data = json::parse(f);
json
dari literal JSON Asumsikan Anda ingin membuat kode keras nilai JSON literal ini dalam sebuah file, sebagai objek json
:
{
"pi" : 3.141 ,
"happy" : true
}
Ada berbagai pilihan:
// Using (raw) string literals and json::parse
json ex1 = json::parse( R"(
{
"pi": 3.141,
"happy": true
}
)" );
// Using user-defined (raw) string literals
using namespace nlohmann ::literals ;
json ex2 = R"(
{
"pi": 3.141,
"happy": true
}
)" _json;
// Using initializer lists
json ex3 = {
{ " happy " , true },
{ " pi " , 3.141 },
};
Berikut adalah beberapa contoh untuk memberi Anda gambaran bagaimana menggunakan kelas.
Asumsikan Anda ingin membuat objek JSON
{
"pi" : 3.141 ,
"happy" : true ,
"name" : " Niels " ,
"nothing" : null ,
"answer" : {
"everything" : 42
},
"list" : [ 1 , 0 , 2 ],
"object" : {
"currency" : " USD " ,
"value" : 42.99
}
}
Dengan perpustakaan ini, Anda dapat menulis:
// create an empty structure (null)
json j;
// add a number that is stored as double (note the implicit conversion of j to an object)
j[ " pi " ] = 3.141 ;
// add a Boolean that is stored as bool
j[ " happy " ] = true ;
// add a string that is stored as std::string
j[ " name " ] = " Niels " ;
// add another null object by passing nullptr
j[ " nothing " ] = nullptr ;
// add an object inside the object
j[ " answer " ][ " everything " ] = 42 ;
// add an array that is stored as std::vector (using an initializer list)
j[ " list " ] = { 1 , 0 , 2 };
// add another object (using an initializer list of pairs)
j[ " object " ] = { { " currency " , " USD " }, { " value " , 42.99 } };
// instead, you could also write (which looks very similar to the JSON above)
json j2 = {
{ " pi " , 3.141 },
{ " happy " , true },
{ " name " , " Niels " },
{ " nothing " , nullptr },
{ " answer " , {
{ " everything " , 42 }
}},
{ " list " , { 1 , 0 , 2 }},
{ " object " , {
{ " currency " , " USD " },
{ " value " , 42.99 }
}}
};
Perhatikan bahwa dalam semua kasus ini, Anda tidak perlu "memberi tahu" kompiler jenis nilai JSON mana yang ingin Anda gunakan. Jika Anda ingin lebih eksplisit atau mengekspresikan beberapa kasus tepi, fungsi json::array()
dan json::object()
akan membantu:
// a way to express the empty array []
json empty_array_explicit = json::array();
// ways to express the empty object {}
json empty_object_implicit = json({});
json empty_object_explicit = json::object();
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
json array_not_object = json::array({ { " currency " , " USD " }, { " value " , 42.99 } });
Anda dapat membuat nilai JSON (deserialisasi) dengan menambahkan _json
ke string literal:
// create object from string literal
json j = " { " happy " : true, " pi " : 3.141 } " _json;
// or even nicer with a raw string literal
auto j2 = R"(
{
"happy": true,
"pi": 3.141
}
)" _json;
Perhatikan bahwa tanpa menambahkan akhiran _json
, literal string yang diteruskan tidak diuraikan, tetapi hanya digunakan sebagai nilai string JSON. Yaitu, json j = "{ "happy": true, "pi": 3.141 }"
hanya akan menyimpan string "{ "happy": true, "pi": 3.141 }"
daripada menguraikan yang sebenarnya obyek.
String literal harus dimasukkan ke dalam cakupan dengan using namespace nlohmann::literals;
(lihat json::parse()
).
Contoh di atas juga dapat diungkapkan secara eksplisit menggunakan json::parse()
:
// parse explicitly
auto j3 = json::parse( R"( {"happy": true, "pi": 3.141} )" );
Anda juga bisa mendapatkan representasi string dari nilai JSON (bersambung):
// explicit conversion to string
std::string s = j.dump(); // {"happy":true,"pi":3.141}
// serialization with pretty printing
// pass in the amount of spaces to indent
std::cout << j.dump( 4 ) << std::endl;
// {
// "happy": true,
// "pi": 3.141
// }
Perhatikan perbedaan antara serialisasi dan penugasan:
// store a string in a JSON value
json j_string = " this is a string " ;
// retrieve the string value
auto cpp_string = j_string. template get<std::string>();
// retrieve the string value (alternative when a variable already exists)
std::string cpp_string2;
j_string.get_to(cpp_string2);
// retrieve the serialized value (explicit JSON serialization)
std::string serialized_string = j_string.dump();
// output of original string
std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string. template get<std::string>() << ' n ' ;
// output of serialized value
std::cout << j_string << " == " << serialized_string << std::endl;
.dump()
mengembalikan nilai string yang awalnya disimpan.
Perhatikan bahwa perpustakaan hanya mendukung UTF-8. Saat Anda menyimpan string dengan pengkodean berbeda di perpustakaan, pemanggilan dump()
mungkin memunculkan pengecualian kecuali json::error_handler_t::replace
atau json::error_handler_t::ignore
digunakan sebagai penangan kesalahan.
Anda juga dapat menggunakan aliran untuk membuat serialisasi dan deserialisasi:
// deserialize from standard input
json j;
std::cin >> j;
// serialize to standard output
std::cout << j;
// the setw manipulator was overloaded to set the indentation for pretty printing
std::cout << std::setw( 4 ) << j << std::endl;
Operator ini berfungsi untuk semua subkelas std::istream
atau std::ostream
. Berikut adalah contoh yang sama dengan file:
// read a JSON file
std::ifstream i ( " file.json " );
json j;
i >> j;
// write prettified JSON to another file
std::ofstream o ( " pretty.json " );
o << std::setw( 4 ) << j << std::endl;
Harap dicatat bahwa pengaturan bit pengecualian untuk failbit
tidak sesuai untuk kasus penggunaan ini. Ini akan mengakibatkan penghentian program karena penentu noexcept
sedang digunakan.
Anda juga dapat mengurai JSON dari rentang iterator; yaitu, dari wadah apa pun yang dapat diakses oleh iterator yang value_type
-nya merupakan tipe integral 1, 2, atau 4 byte, yang masing-masing akan ditafsirkan sebagai UTF-8, UTF-16, dan UTF-32. Misalnya, std::vector<std::uint8_t>
, atau std::list<std::uint16_t>
:
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v.begin(), v.end());
Anda dapat meninggalkan iterator untuk rentang [mulai, akhir):
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v);
Karena fungsi parse menerima rentang iterator arbitrer, Anda dapat menyediakan sumber data Anda sendiri dengan menerapkan konsep LegacyInputIterator
.
struct MyContainer {
void advance ();
const char & get_current ();
};
struct MyIterator {
using difference_type = std:: ptrdiff_t ;
using value_type = char ;
using pointer = const char *;
using reference = const char &;
using iterator_category = std::input_iterator_tag;
MyIterator& operator ++() {
target-> advance ();
return * this ;
}
bool operator !=( const MyIterator& rhs) const {
return rhs. target != target;
}
reference operator *() const {
return target-> get_current ();
}
MyContainer* target = nullptr ;
};
MyIterator begin (MyContainer& tgt) {
return MyIterator{&tgt};
}
MyIterator end ( const MyContainer&) {
return {};
}
void foo () {
MyContainer c;
json j = json::parse (c);
}
Perpustakaan menggunakan antarmuka mirip SAX dengan fungsi berikut:
// called when null is parsed
bool null ();
// called when a boolean is parsed; value is passed
bool boolean ( bool val);
// called when a signed or unsigned integer number is parsed; value is passed
bool number_integer ( number_integer_t val);
bool number_unsigned ( number_unsigned_t val);
// called when a floating-point number is parsed; value and original string is passed
bool number_float ( number_float_t val, const string_t & s);
// called when a string is parsed; value is passed and can be safely moved away
bool string ( string_t & val);
// called when a binary value is parsed; value is passed and can be safely moved away
bool binary ( binary_t & val);
// called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known)
bool start_object (std:: size_t elements);
bool end_object ();
bool start_array (std:: size_t elements);
bool end_array ();
// called when an object key is parsed; value is passed and can be safely moved away
bool key ( string_t & val);
// called when a parse error occurs; byte position, the last token, and an exception is passed
bool parse_error (std:: size_t position, const std::string& last_token, const detail:: exception & ex);
Nilai kembalian setiap fungsi menentukan apakah penguraian harus dilanjutkan.
Untuk mengimplementasikan pengendali SAX Anda sendiri, lakukan sebagai berikut:
nlohmann::json_sax<json>
sebagai kelas dasar, tetapi Anda juga dapat menggunakan kelas mana pun yang fungsi yang dijelaskan di atas diimplementasikan dan bersifat publik.my_sax
.bool json::sax_parse(input, &my_sax)
; di mana parameter pertama dapat berupa masukan apa pun seperti string atau aliran masukan dan parameter kedua adalah penunjuk ke antarmuka SAX Anda. Perhatikan bahwa fungsi sax_parse
hanya mengembalikan bool
yang menunjukkan hasil acara SAX yang terakhir dieksekusi. Itu tidak mengembalikan nilai json
- terserah Anda untuk memutuskan apa yang harus dilakukan dengan acara SAX. Selain itu, tidak ada pengecualian yang diberikan jika terjadi kesalahan parse - terserah Anda apa yang harus dilakukan dengan objek pengecualian yang diteruskan ke implementasi parse_error
Anda. Secara internal, antarmuka SAX digunakan untuk parser DOM (kelas json_sax_dom_parser
) serta akseptor ( json_sax_acceptor
), lihat file json_sax.hpp
.
Kami merancang kelas JSON untuk berperilaku seperti wadah STL. Faktanya, ini memenuhi persyaratan ReversibleContainer .
// create an array using push_back
json j;
j.push_back( " foo " );
j.push_back( 1 );
j.push_back( true );
// also use emplace_back
j.emplace_back( 1.78 );
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
std::cout << *it << ' n ' ;
}
// range-based for
for ( auto & element : j) {
std::cout << element << ' n ' ;
}
// getter/setter
const auto tmp = j[ 0 ]. template get<std::string>();
j[ 1 ] = 42 ;
bool foo = j.at( 2 );
// comparison
j == R"( ["foo", 1, true, 1.78] )" _json; // true
// other stuff
j.size(); // 4 entries
j.empty(); // false
j.type(); // json::value_t::array
j.clear(); // the array is empty again
// convenience type checkers
j.is_null();
j.is_boolean();
j.is_number();
j.is_object();
j.is_array();
j.is_string();
// create an object
json o;
o[ " foo " ] = 23 ;
o[ " bar " ] = false ;
o[ " baz " ] = 3.141 ;
// also use emplace
o.emplace( " weather " , " sunny " );
// special iterator member functions for objects
for (json::iterator it = o.begin(); it != o.end(); ++it) {
std::cout << it. key () << " : " << it. value () << " n " ;
}
// the same code as range for
for ( auto & el : o.items()) {
std::cout << el. key () << " : " << el. value () << " n " ;
}
// even easier with structured bindings (C++17)
for ( auto & [key, value] : o.items()) {
std::cout << key << " : " << value << " n " ;
}
// find an entry
if (o.contains( " foo " )) {
// there is an entry with key "foo"
}
// or via find and an iterator
if (o.find( " foo " ) != o.end()) {
// there is an entry with key "foo"
}
// or simpler using count()
int foo_present = o.count( " foo " ); // 1
int fob_present = o.count( " fob " ); // 0
// delete an entry
o.erase( " foo " );
Wadah urutan apa pun ( std::array
, std::vector
, std::deque
, std::forward_list
, std::list
) yang nilainya dapat digunakan untuk membuat nilai JSON (misalnya bilangan bulat, angka floating point, Boolean, string jenis, atau lagi wadah STL yang dijelaskan di bagian ini) dapat digunakan untuk membuat array JSON. Hal yang sama berlaku untuk wadah asosiatif serupa ( std::set
, std::multiset
, std::unordered_set
, std::unordered_multiset
), tetapi dalam kasus ini urutan elemen array bergantung pada bagaimana elemen diurutkan dalam wadah STL masing-masing.
std::vector< int > c_vector { 1 , 2 , 3 , 4 };
json j_vec (c_vector);
// [1, 2, 3, 4]
std::deque< double > c_deque { 1.2 , 2.3 , 3.4 , 5.6 };
json j_deque (c_deque);
// [1.2, 2.3, 3.4, 5.6]
std::list< bool > c_list { true , true , false , true };
json j_list (c_list);
// [true, true, false, true]
std::forward_list< int64_t > c_flist { 12345678909876 , 23456789098765 , 34567890987654 , 45678909876543 };
json j_flist (c_flist);
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]
std::array< unsigned long , 4 > c_array {{ 1 , 2 , 3 , 4 }};
json j_array (c_array);
// [1, 2, 3, 4]
std::set<std::string> c_set { " one " , " two " , " three " , " four " , " one " };
json j_set (c_set); // only one entry for "one" is used
// ["four", "one", "three", "two"]
std::unordered_set<std::string> c_uset { " one " , " two " , " three " , " four " , " one " };
json j_uset (c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]
std::multiset<std::string> c_mset { " one " , " two " , " one " , " four " };
json j_mset (c_mset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
std::unordered_multiset<std::string> c_umset { " one " , " two " , " one " , " four " };
json j_umset (c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
Demikian pula, semua wadah nilai kunci asosiatif ( std::map
, std::multimap
, std::unordered_map
, std::unordered_multimap
) yang kuncinya dapat membuat std::string
dan yang nilainya dapat digunakan untuk membuat nilai JSON (lihat contoh di atas) dapat digunakan untuk membuat objek JSON. Perhatikan bahwa dalam kasus multimap hanya satu kunci yang digunakan dalam objek JSON dan nilainya bergantung pada urutan internal wadah STL.
std::map<std::string, int > c_map { { " one " , 1 }, { " two " , 2 }, { " three " , 3 } };
json j_map (c_map);
// {"one": 1, "three": 3, "two": 2 }
std::unordered_map< const char *, double > c_umap { { " one " , 1.2 }, { " two " , 2.3 }, { " three " , 3.4 } };
json j_umap (c_umap);
// {"one": 1.2, "two": 2.3, "three": 3.4}
std::multimap<std::string, bool > c_mmap { { " one " , true }, { " two " , true }, { " three " , false }, { " three " , true } };
json j_mmap (c_mmap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
std::unordered_multimap<std::string, bool > c_ummap { { " one " , true }, { " two " , true }, { " three " , false }, { " three " , true } };
json j_ummap (c_ummap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
Pustaka mendukung JSON Pointer (RFC 6901) sebagai sarana alternatif untuk menangani nilai terstruktur. Selain itu, JSON Patch (RFC 6902) memungkinkan penjelasan perbedaan antara dua nilai JSON - secara efektif memungkinkan operasi patch dan diff yang diketahui dari Unix.
// a JSON value
json j_original = R"( {
"baz": ["one", "two", "three"],
"foo": "bar"
} )" _json;
// access members with a JSON pointer (RFC 6901)
j_original[ " /baz/1 " _json_pointer];
// "two"
// a JSON patch (RFC 6902)
json j_patch = R"( [
{ "op": "replace", "path": "/baz", "value": "boo" },
{ "op": "add", "path": "/hello", "value": ["world"] },
{ "op": "remove", "path": "/foo"}
] )" _json;
// apply the patch
json j_result = j_original.patch(j_patch);
// {
// "baz": "boo",
// "hello": ["world"]
// }
// calculate a JSON patch from two JSON values
json::diff (j_result, j_original);
// [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
// { "op": "remove","path": "/hello" },
// { "op": "add", "path": "/foo", "value": "bar" }
// ]
Pustaka mendukung JSON Merge Patch (RFC 7386) sebagai format patch. Alih-alih menggunakan JSON Pointer (lihat di atas) untuk menentukan nilai yang akan dimanipulasi, ini menjelaskan perubahan menggunakan sintaksis yang sangat mirip dengan dokumen yang sedang dimodifikasi.
// a JSON value
json j_document = R"( {
"a": "b",
"c": {
"d": "e",
"f": "g"
}
} )" _json;
// a patch
json j_patch = R"( {
"a":"z",
"c": {
"f": null
}
} )" _json;
// apply the patch
j_document.merge_patch(j_patch);
// {
// "a": "z",
// "c": {
// "d": "e"
// }
// }
Tipe yang didukung dapat dikonversi secara implisit ke nilai JSON.
Disarankan untuk TIDAK MENGGUNAKAN konversi implisit DARI nilai JSON. Anda dapat menemukan detail lebih lanjut tentang rekomendasi ini di sini. Anda dapat menonaktifkan konversi implisit dengan menentukan JSON_USE_IMPLICIT_CONVERSIONS
ke 0
sebelum menyertakan header json.hpp
. Saat menggunakan CMake, Anda juga dapat mencapainya dengan menyetel opsi JSON_ImplicitConversions
ke OFF
.
// strings
std::string s1 = " Hello, world! " ;
json js = s1;
auto s2 = js. template get<std::string>();
// NOT RECOMMENDED
std::string s3 = js;
std::string s4;
s4 = js;
// Booleans
bool b1 = true ;
json jb = b1;
auto b2 = jb. template get< bool >();
// NOT RECOMMENDED
bool b3 = jb;
bool b4;
b4 = jb;
// numbers
int i = 42