json
من حرفية JSONهناك عدد لا يحصى من مكتبات JSON، وربما يكون لكل منها سبب وجودها. كان لفصلنا أهداف التصميم التالية:
بناء الجملة بديهية . في لغات مثل Python، يبدو JSON وكأنه نوع بيانات من الدرجة الأولى. لقد استخدمنا كل سحر المشغل في لغة C++ الحديثة لتحقيق نفس الشعور في التعليمات البرمجية الخاصة بك. تحقق من الأمثلة أدناه وستعرف ما أعنيه.
التكامل التافه . يتكون الكود الخاص بنا بالكامل من ملف رأس واحد json.hpp
. هذا كل شيء. لا توجد مكتبة ولا مشروع فرعي ولا تبعيات ولا نظام بناء معقد. الفصل مكتوب بالفانيليا C++ 11. بشكل عام، يجب ألا يتطلب كل شيء أي تعديل لعلامات المترجم أو إعدادات المشروع.
اختبار جدي . تم اختبار الكود الخاص بنا بشكل مكثف على الوحدات ويغطي 100% من الكود، بما في ذلك جميع السلوكيات الاستثنائية. علاوة على ذلك، تحققنا مع Valgrind وClang Sanitizers من عدم وجود تسرب للذاكرة. بالإضافة إلى ذلك، يقوم Google OSS-Fuzz بإجراء اختبارات ضبابية ضد جميع المحللين اللغويين على مدار الساعة طوال أيام الأسبوع، مما يؤدي إلى تنفيذ مليارات الاختبارات بشكل فعال حتى الآن. وللحفاظ على الجودة العالية، يتبع المشروع أفضل ممارسات مبادرة البنية التحتية الأساسية (CII).
الجوانب الأخرى لم تكن ذات أهمية كبيرة بالنسبة لنا:
كفاءة الذاكرة . يحتوي كل كائن JSON على مؤشر واحد (الحجم الأقصى للاتحاد) وعنصر تعداد واحد (1 بايت). يستخدم التعميم الافتراضي أنواع بيانات C++ التالية: std::string
للسلاسل، int64_t
، uint64_t
أو double
للأرقام، std::map
للكائنات، std::vector
للمصفوفات، و bool
للقيم المنطقية. ومع ذلك، يمكنك تصميم الفئة المعممة basic_json
وفقًا لاحتياجاتك.
سرعة . هناك بالتأكيد مكتبات JSON أسرع. ومع ذلك، إذا كان هدفك هو تسريع عملية التطوير الخاصة بك عن طريق إضافة دعم JSON برأس واحد، فهذه المكتبة هي الحل الأمثل. إذا كنت تعرف كيفية استخدام std::vector
أو std::map
، فقد تم تعيينك بالفعل.
راجع إرشادات المساهمة لمزيد من المعلومات.
يمكنك رعاية هذه المكتبة لدى رعاة GitHub.
شكرا للجميع!
❓ إذا كان لديك سؤال ، يرجى التحقق مما إذا تمت الإجابة عليه بالفعل في قسم الأسئلة الشائعة أو قسم الأسئلة والأجوبة . إذا لم يكن الأمر كذلك، يرجى طرح سؤال جديد هناك.
إذا كنت تريد معرفة المزيد حول كيفية استخدام المكتبة، فاطلع على بقية الملف README ، أو ألق نظرة على أمثلة التعليمات البرمجية ، أو تصفح صفحات المساعدة .
؟ إذا كنت تريد فهم واجهة برمجة التطبيقات (API) بشكل أفضل، فاطلع على مرجع واجهة برمجة التطبيقات (API) .
؟ إذا وجدت خطأ ما ، فيرجى مراجعة الأسئلة الشائعة إذا كانت مشكلة معروفة أو نتيجة لقرار تصميم. يرجى أيضًا إلقاء نظرة على قائمة المشكلات قبل إنشاء إصدار جديد . يرجى تقديم أكبر قدر ممكن من المعلومات لمساعدتنا في فهم مشكلتك وإعادة إنتاجها.
توجد أيضًا مجموعة مستندات لمتصفحات التوثيق Dash وVelocity وZeal التي تحتوي على الوثائق الكاملة كمورد غير متصل بالإنترنت.
فيما يلي بعض الأمثلة لإعطائك فكرة عن كيفية استخدام الفصل.
بجانب الأمثلة أدناه، قد ترغب في:
→ التحقق من الوثائق
→ تصفح ملفات الأمثلة المستقلة
تحتوي كل وظيفة API (موثقة في وثائق API) على ملف مثال مستقل مطابق. على سبيل المثال، تحتوي الدالة emplace()
على ملف مثال emplace.cpp مطابق.
توفر فئة json
واجهة برمجة تطبيقات لمعالجة قيمة JSON. لإنشاء كائن json
من خلال قراءة ملف JSON:
# include < fstream >
# include < nlohmann/json.hpp >
using json = nlohmann::json;
// ...
std::ifstream f ( " example.json " );
json data = json::parse(f);
json
من حرفية JSON افترض أنك تريد إنشاء رمز ثابت لقيمة JSON الحرفية هذه في ملف، ككائن json
:
{
"pi" : 3.141 ,
"happy" : true
}
هناك خيارات مختلفة:
// 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 },
};
فيما يلي بعض الأمثلة لإعطائك فكرة عن كيفية استخدام الفصل.
افترض أنك تريد إنشاء كائن JSON
{
"pi" : 3.141 ,
"happy" : true ,
"name" : " Niels " ,
"nothing" : null ,
"answer" : {
"everything" : 42
},
"list" : [ 1 , 0 , 2 ],
"object" : {
"currency" : " USD " ,
"value" : 42.99
}
}
مع هذه المكتبة، يمكنك أن تكتب:
// 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 }
}}
};
لاحظ أنه في كل هذه الحالات، لن تحتاج أبدًا إلى "إخبار" المترجم بنوع قيمة JSON الذي تريد استخدامه. إذا كنت تريد أن تكون صريحًا أو تعبر عن بعض حالات الحافة، فستساعدك الدالتان json::array()
و json::object()
:
// 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 } });
يمكنك إنشاء قيمة JSON (إلغاء التسلسل) عن طريق إلحاق _json
بسلسلة حرفية:
// 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;
لاحظ أنه بدون إلحاق اللاحقة _json
، لا يتم تحليل السلسلة الحرفية التي تم تمريرها، ولكن يتم استخدامها فقط كقيمة سلسلة JSON. وهذا يعني أن json j = "{ "happy": true, "pi": 3.141 }"
سيخزن فقط السلسلة "{ "happy": true, "pi": 3.141 }"
بدلاً من تحليل السلسلة الفعلية هدف.
يجب وضع السلسلة الحرفية في النطاق using namespace nlohmann::literals;
(انظر json::parse()
).
يمكن أيضًا التعبير عن المثال أعلاه بشكل صريح باستخدام json::parse()
:
// parse explicitly
auto j3 = json::parse( R"( {"happy": true, "pi": 3.141} )" );
يمكنك أيضًا الحصول على تمثيل سلسلة لقيمة JSON (تسلسل):
// 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
// }
لاحظ الفرق بين التسلسل والمهمة:
// 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()
بإرجاع قيمة السلسلة المخزنة في الأصل.
لاحظ أن المكتبة تدعم UTF-8 فقط. عندما تقوم بتخزين سلاسل ذات ترميزات مختلفة في المكتبة، فإن استدعاء dump()
قد يؤدي إلى استثناء ما لم يتم json::error_handler_t::replace
أو json::error_handler_t::ignore
كمعالجات للأخطاء.
يمكنك أيضًا استخدام التدفقات للتسلسل وإلغاء التسلسل:
// 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;
تعمل هذه العوامل مع أي فئات فرعية من std::istream
أو std::ostream
. هنا نفس المثال مع الملفات:
// 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;
يرجى ملاحظة أن تعيين بت الاستثناء لـ failbit
غير مناسب لحالة الاستخدام هذه. سيؤدي ذلك إلى إنهاء البرنامج بسبب محدد noexcept
قيد الاستخدام.
يمكنك أيضًا تحليل JSON من نطاق التكرار؛ أي من أي حاوية يمكن الوصول إليها بواسطة التكرارات التي يكون value_type
الخاص بها نوعًا متكاملًا مكونًا من 1 أو 2 أو 4 بايت، والذي سيتم تفسيره على أنه UTF-8 وUTF-16 وUTF-32 على التوالي. على سبيل المثال، std::vector<std::uint8_t>
أو std::list<std::uint16_t>
:
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v.begin(), v.end());
يمكنك ترك التكرارات للنطاق [البداية والنهاية):
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v);
نظرًا لأن وظيفة التحليل تقبل نطاقات مكررة عشوائية، يمكنك توفير مصادر البيانات الخاصة بك عن طريق تطبيق مفهوم 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);
}
تستخدم المكتبة واجهة تشبه SAX مع الوظائف التالية:
// 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);
تحدد القيمة المرجعة لكل دالة ما إذا كان يجب متابعة التحليل أم لا.
لتنفيذ معالج SAX الخاص بك، تابع كما يلي:
nlohmann::json_sax<json>
كفئة أساسية، ولكن يمكنك أيضًا استخدام أي فئة يتم فيها تنفيذ الوظائف الموضحة أعلاه وتكون عامة.my_sax
.bool json::sax_parse(input, &my_sax)
; حيث يمكن أن تكون المعلمة الأولى أي إدخال مثل سلسلة أو دفق إدخال والمعلمة الثانية هي مؤشر لواجهة SAX الخاصة بك. لاحظ أن الدالة sax_parse
تقوم فقط بإرجاع bool
يشير إلى نتيجة آخر حدث SAX تم تنفيذه. ولا يُرجع قيمة json
- الأمر متروك لك لتقرر ما يجب فعله بأحداث SAX. علاوة على ذلك، لا يتم طرح أي استثناءات في حالة حدوث خطأ في التحليل - الأمر متروك لك فيما يجب فعله بكائن الاستثناء الذي تم تمريره إلى تطبيق parse_error
الخاص بك. داخليًا، يتم استخدام واجهة SAX لمحلل DOM (الفئة json_sax_dom_parser
) بالإضافة إلى المتقبل ( json_sax_acceptor
)، راجع الملف json_sax.hpp
.
لقد صممنا فئة JSON لتتصرف تمامًا مثل حاوية STL. في الواقع، إنها تلبي متطلبات 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 " );
أي حاوية تسلسل ( std::array
, std::vector
, std::deque
, std::forward_list
, std::list
) يمكن استخدام قيمها لإنشاء قيم JSON (على سبيل المثال، الأعداد الصحيحة وأرقام الفاصلة العائمة والقيم المنطقية والسلسلة يمكن استخدام الأنواع، أو حاويات STL الموضحة في هذا القسم مرة أخرى) لإنشاء مصفوفة JSON. وينطبق الشيء نفسه على الحاويات الترابطية المماثلة ( std::set
, std::multiset
, std::unordered_set
, std::unordered_multiset
) ، ولكن في هذه الحالات يعتمد ترتيب عناصر المصفوفة على كيفية ترتيب العناصر في حاوية STL المعنية.
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"]
وبالمثل، أي حاويات ذات قيمة مفتاح مرتبطة ( std::map
, std::multimap
, std::unordered_map
, std::unordered_multimap
) التي يمكن لمفاتيحها إنشاء std::string
ويمكن استخدام قيمها لإنشاء قيم JSON (راجع الأمثلة المذكورة أعلاه) يمكن استخدامها لإنشاء كائن JSON. لاحظ أنه في حالة الخرائط المتعددة، يتم استخدام مفتاح واحد فقط في كائن JSON وتعتمد القيمة على الترتيب الداخلي لحاوية 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}
تدعم المكتبة مؤشر JSON (RFC 6901) كوسيلة بديلة لمعالجة القيم المنظمة. علاوة على ذلك، يسمح JSON Patch (RFC 6902) بوصف الاختلافات بين قيمتين JSON - مما يسمح بشكل فعال بعمليات التصحيح والفرق المعروفة من 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" }
// ]
تدعم المكتبة JSON Merge Patch (RFC 7386) كتنسيق تصحيح. بدلاً من استخدام مؤشر JSON (انظر أعلاه) لتحديد القيم المراد معالجتها، فإنه يصف التغييرات باستخدام بناء جملة يحاكي بشكل وثيق المستند الذي يتم تعديله.
// 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"
// }
// }
يمكن تحويل الأنواع المدعومة ضمنيًا إلى قيم JSON.
يوصى بعدم استخدام التحويلات الضمنية من قيمة JSON. يمكنك العثور على مزيد من التفاصيل حول هذه التوصية هنا. يمكنك إيقاف التحويلات الضمنية عن طريق تحديد JSON_USE_IMPLICIT_CONVERSIONS
إلى 0
قبل تضمين رأس json.hpp
. عند استخدام CMake، يمكنك أيضًا تحقيق ذلك عن طريق ضبط الخيار JSON_ImplicitConversions
على 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