json
a partir de literales JSONExisten innumerables bibliotecas JSON y cada una puede incluso tener su razón de existir. Nuestra clase tenía estos objetivos de diseño:
Sintaxis intuitiva . En lenguajes como Python, JSON se siente como un tipo de datos de primera clase. Usamos toda la magia de los operadores del C++ moderno para lograr la misma sensación en su código. Mira los ejemplos a continuación y sabrás a qué me refiero.
Integración trivial . Todo nuestro código consta de un único archivo de encabezado json.hpp
. Eso es todo. Sin biblioteca, sin subproyectos, sin dependencias, sin sistema de compilación complejo. La clase está escrita en Vanilla C++11. Con todo, todo no debería requerir ningún ajuste de los indicadores del compilador o de la configuración del proyecto.
Pruebas serias . Nuestro código se somete a pruebas unitarias exhaustivas y cubre el 100% del código, incluido todo el comportamiento excepcional. Además, verificamos con Valgrind y Clang Sanitizers que no hay pérdidas de memoria. Google OSS-Fuzz además ejecuta pruebas fuzz contra todos los analizadores las 24 horas del día, los 7 días de la semana, ejecutando efectivamente miles de millones de pruebas hasta el momento. Para mantener una alta calidad, el proyecto sigue las mejores prácticas de la Iniciativa de Infraestructura Básica (CII).
Otros aspectos no fueron tan importantes para nosotros:
Eficiencia de la memoria . Cada objeto JSON tiene una sobrecarga de un puntero (el tamaño máximo de una unión) y un elemento de enumeración (1 byte). La generalización predeterminada utiliza los siguientes tipos de datos de C++: std::string
para cadenas, int64_t
, uint64_t
o double
para números, std::map
para objetos, std::vector
para matrices y bool
para booleanos. Sin embargo, puede crear una plantilla para la clase generalizada basic_json
según sus necesidades.
Velocidad . Ciertamente existen bibliotecas JSON más rápidas. Sin embargo, si su objetivo es acelerar su desarrollo agregando compatibilidad con JSON con un solo encabezado, entonces esta biblioteca es el camino a seguir. Si sabes cómo usar std::vector
o std::map
, ya estás listo.
Consulte las pautas de contribución para obtener más información.
Puedes patrocinar esta biblioteca en Patrocinadores de GitHub.
¡Gracias a todos!
❓ Si tiene alguna pregunta , verifique si ya está respondida en las preguntas frecuentes o en la sección de preguntas y respuestas . Si no es así, haga una nueva pregunta allí.
Si desea obtener más información sobre cómo utilizar la biblioteca, consulte el resto del archivo README , eche un vistazo a los ejemplos de código o explore las páginas de ayuda .
? Si desea comprender mejor la API , consulte la Referencia de API .
? Si encontró un error , consulte las preguntas frecuentes si se trata de un problema conocido o el resultado de una decisión de diseño. Eche también un vistazo a la lista de problemas antes de crear un nuevo problema . Proporcione tanta información como sea posible para ayudarnos a comprender y reproducir su problema.
También hay un conjunto de documentos para los navegadores de documentación Dash, Velocity y Zeal que contiene la documentación completa como recurso sin conexión.
A continuación se muestran algunos ejemplos para darle una idea de cómo utilizar la clase.
Además de los ejemplos siguientes, es posible que desee:
→ Consulta la documentación
→ Explore los archivos de ejemplo independientes
Cada función de API (documentada en la documentación de API) tiene un archivo de ejemplo independiente correspondiente. Por ejemplo, la función emplace()
tiene un archivo de ejemplo emplace.cpp coincidente.
La clase json
proporciona una API para manipular un valor JSON. Para crear un objeto json
leyendo un archivo JSON:
# include < fstream >
# include < nlohmann/json.hpp >
using json = nlohmann::json;
// ...
std::ifstream f ( " example.json " );
json data = json::parse(f);
json
a partir de literales JSON Supongamos que desea crear un código rígido de este valor JSON literal en un archivo, como un objeto json
:
{
"pi" : 3.141 ,
"happy" : true
}
Hay varias opciones:
// 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 },
};
A continuación se muestran algunos ejemplos para darle una idea de cómo utilizar la clase.
Supongamos que desea crear el objeto JSON.
{
"pi" : 3.141 ,
"happy" : true ,
"name" : " Niels " ,
"nothing" : null ,
"answer" : {
"everything" : 42
},
"list" : [ 1 , 0 , 2 ],
"object" : {
"currency" : " USD " ,
"value" : 42.99
}
}
Con esta biblioteca, podrías escribir:
// 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 }
}}
};
Tenga en cuenta que en todos estos casos, nunca necesitará "decirle" al compilador qué tipo de valor JSON desea utilizar. Si desea ser explícito o expresar algunos casos extremos, las funciones json::array()
y json::object()
le ayudarán:
// 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 } });
Puede crear un valor JSON (deserialización) agregando _json
a una cadena 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;
Tenga en cuenta que sin agregar el sufijo _json
, el literal de cadena pasado no se analiza, sino que simplemente se usa como valor de cadena JSON. Es decir, json j = "{ "happy": true, "pi": 3.141 }"
simplemente almacenaría la cadena "{ "happy": true, "pi": 3.141 }"
en lugar de analizar la cadena real objeto.
El literal de cadena debe incluirse en el alcance mediante using namespace nlohmann::literals;
(ver json::parse()
).
El ejemplo anterior también se puede expresar explícitamente usando json::parse()
:
// parse explicitly
auto j3 = json::parse( R"( {"happy": true, "pi": 3.141} )" );
También puede obtener una representación de cadena de un valor JSON (serializar):
// 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
// }
Tenga en cuenta la diferencia entre serialización y asignación:
// 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()
devuelve el valor de cadena almacenado originalmente.
Tenga en cuenta que la biblioteca solo admite UTF-8. Cuando almacena cadenas con diferentes codificaciones en la biblioteca, llamar dump()
puede generar una excepción a menos que json::error_handler_t::replace
o json::error_handler_t::ignore
se utilicen como controladores de errores.
También puede utilizar transmisiones para serializar y deserializar:
// 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;
Estos operadores funcionan para cualquier subclase de std::istream
o std::ostream
. Aquí está el mismo ejemplo con archivos:
// 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;
Tenga en cuenta que configurar el bit de excepción para failbit
no es apropiado para este caso de uso. Resultará en la terminación del programa debido al uso del especificador noexcept
.
También puedes analizar JSON desde un rango de iteradores; es decir, desde cualquier contenedor accesible por iteradores cuyo value_type
sea un tipo integral de 1, 2 o 4 bytes, que se interpretará como UTF-8, UTF-16 y UTF-32 respectivamente. Por ejemplo, un std::vector<std::uint8_t>
, o un std::list<std::uint16_t>
:
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v.begin(), v.end());
Puedes dejar los iteradores para el rango [comienzo, fin):
std::vector<std:: uint8_t > v = { ' t ' , ' r ' , ' u ' , ' e ' };
json j = json::parse(v);
Dado que la función de análisis acepta rangos de iteradores arbitrarios, puede proporcionar sus propias fuentes de datos implementando el concepto 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);
}
La biblioteca utiliza una interfaz tipo SAX con las siguientes funciones:
// 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);
El valor de retorno de cada función determina si se debe continuar con el análisis.
Para implementar su propio controlador SAX, proceda de la siguiente manera:
nlohmann::json_sax<json>
como clase base, pero también puede usar cualquier clase donde las funciones descritas anteriormente estén implementadas y sean públicas.my_sax
.bool json::sax_parse(input, &my_sax)
; donde el primer parámetro puede ser cualquier entrada como una cadena o un flujo de entrada y el segundo parámetro es un puntero a su interfaz SAX. Tenga en cuenta que la función sax_parse
solo devuelve un bool
que indica el resultado del último evento SAX ejecutado. No devuelve un valor json
; usted decide qué hacer con los eventos SAX. Además, no se generan excepciones en caso de un error de análisis; depende de usted qué hacer con el objeto de excepción pasado a su implementación parse_error
. Internamente, la interfaz SAX se utiliza para el analizador DOM (clase json_sax_dom_parser
), así como para el aceptador ( json_sax_acceptor
), consulte el archivo json_sax.hpp
.
Diseñamos la clase JSON para que se comporte como un contenedor STL. De hecho, satisface el requisito de 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 " );
Cualquier contenedor de secuencia ( std::array
, std::vector
, std::deque
, std::forward_list
, std::list
) cuyos valores se puedan utilizar para construir valores JSON (por ejemplo, enteros, números de punto flotante, booleanos, cadenas tipos, o nuevamente contenedores STL descritos en esta sección) se pueden usar para crear una matriz JSON. Lo mismo se aplica a contenedores asociativos similares ( std::set
, std::multiset
, std::unordered_set
, std::unordered_multiset
), pero en estos casos el orden de los elementos de la matriz depende de cómo se ordenan los elementos en el contenedor STL respectivo.
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"]
Del mismo modo, cualquier contenedor asociativo de clave-valor ( std::map
, std::multimap
, std::unordered_map
, std::unordered_multimap
) cuyas claves puedan construir un std::string
y cuyos valores se puedan usar para construir valores JSON (consulte ejemplos anteriores) se pueden utilizar para crear un objeto JSON. Tenga en cuenta que en el caso de mapas múltiples solo se usa una clave en el objeto JSON y el valor depende del orden interno del contenedor 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}
La biblioteca admite JSON Pointer (RFC 6901) como medio alternativo para abordar valores estructurados. Además de esto, JSON Patch (RFC 6902) permite describir las diferencias entre dos valores JSON, lo que permite de manera efectiva operaciones de parche y diferenciación conocidas en 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" }
// ]
La biblioteca admite JSON Merge Patch (RFC 7386) como formato de parche. En lugar de utilizar el puntero JSON (ver arriba) para especificar los valores que se van a manipular, describe los cambios utilizando una sintaxis que imita fielmente el documento que se está modificando.
// 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"
// }
// }
Los tipos admitidos se pueden convertir implícitamente a valores JSON.
Se recomienda NO UTILIZAR conversiones implícitas DESDE un valor JSON. Puede encontrar más detalles sobre esta recomendación aquí. Puede desactivar las conversiones implícitas definiendo JSON_USE_IMPLICIT_CONVERSIONS
en 0
antes de incluir el encabezado json.hpp
. Al usar CMake, también puede lograr esto configurando la opción JSON_ImplicitConversions
en 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