jsoncons é uma biblioteca C++ somente de cabeçalho para construir formatos de dados JSON e semelhantes a JSON, como CBOR. Para cada formato de dados suportado, permite trabalhar com os dados de diversas maneiras:
Como uma estrutura de dados semelhante a uma variante e com reconhecimento de alocador, basic_json
Como uma estrutura de dados C++ fortemente tipada que implementa json_type_traits
Com acesso no nível do cursor a um fluxo de eventos de análise, algo análogo à análise pull StAX e serialização push no mundo XML.
Comparado a outras bibliotecas JSON, o jsoncons foi projetado para lidar com textos JSON muito grandes. Em sua essência estão analisadores e serializadores no estilo SAX. Ele suporta a leitura de um texto JSON inteiro na memória em uma estrutura semelhante a uma variante. Mas também suporta acesso eficiente aos dados subjacentes usando análise pull no estilo StAX e serialização push. E suporta análise incremental no formato preferido do usuário, usando informações sobre tipos de usuários fornecidas por especializações de json_type_traits.
O modelo de dados jsoncons suporta os tipos JSON familiares – nulos, booleanos, números, strings, arrays, objetos – além de strings de bytes. Além disso, jsoncons suporta marcação semântica de data e hora, época, números inteiros grandes, decimais grandes, números flutuantes grandes e codificações binárias. Isso permite preservar a semântica desse tipo ao analisar formatos de dados semelhantes a JSON, como CBOR, que os possuem.
jsoncons é distribuído sob a licença de software Boost.
jsoncons é gratuito, mas aceita suporte para sustentar seu desenvolvimento. Se você achar esta biblioteca útil, considere fazer uma doação única ou tornar-se um patrocinador ❤️.
À medida que a biblioteca jsoncons
evoluiu, os nomes às vezes mudaram. Para facilitar a transição, o jsoncons descontinua os nomes antigos, mas continua a oferecer suporte a muitos deles. Os nomes obsoletos podem ser suprimidos definindo a macro JSONCONS_NO_DEPRECATED
e isso é recomendado para novo código.
"O Apache Kvrocks utiliza consistentemente jsoncons para oferecer suporte para estruturas de dados JSON aos usuários. Achamos a experiência de desenvolvimento com jsoncons excelente!"
"Tenho usado sua biblioteca em minha linguagem nativa - R - e criei um pacote R facilitando (a) consultas JMESpath e JSONpath em strings JSON ou objetos R e (b) para outros desenvolvedores R vincularem-se à sua biblioteca ."
"Estou usando sua biblioteca como interface externa para passar dados, além de usar as conversões de csv para json, que são realmente úteis para converter dados para uso em javascript"
“Verifiquei que, para minhas necessidades em JSON e CBOR, está funcionando perfeitamente”
"o recurso JSONPath desta biblioteca é ótimo"
"Usamos a implementação JMESPath bastante"
"Adoramos seu validador de esquema JSON. Estamos usando-o no ER/Studio, nossa ferramenta de modelagem de dados, para analisar arquivos de esquema JSON para que possamos criar modelos de relações de entidade a partir deles."
"a biblioteca de serialização preferida com seus belos mapeamentos e facilidade de uso"
"muito bom" "projeto incrível" "muito sólido e muito confiável" "minha equipe adora" "Seu repositório é demais!!!!!"
Comece a usar conjuntos de imagens e quadros de imagens do HealthImaging usando um AWS SDK
RubyGems.org rjsoncons Licenças Coppelia Robotics CSPro
Você pode usar o gerenciador de biblioteca da plataforma vcpkg para instalar o pacote jsoncons.
Ou baixe a versão mais recente e descompacte o arquivo zip. Copie o diretório include/jsoncons
para seu diretório include
. Se você deseja usar extensões, copie include/jsoncons_ext
também.
Ou baixe o código mais recente em main.
A biblioteca requer um compilador C++ com suporte C++ 11. Além disso, a biblioteca define jsoncons::endian
, jsoncons::basic_string_view
, jsoncons::optional
e jsoncons::span
, que serão digitados para seus equivalentes de biblioteca padrão se detectados. Caso contrário, eles serão digitados para implementações internas compatíveis com C++ 11.
A biblioteca usa exceções e, em alguns casos, std::error_code's para relatar erros. Além de jsoncons::assertion_error
, todas as classes de exceção jsoncons implementam a interface jsoncons::json_error. Se as exceções estiverem desabilitadas ou se a macro de tempo de compilação JSONCONS_NO_EXCEPTIONS
for definida, os lançamentos se tornarão chamadas para std::terminate
.
json_benchmarks fornece algumas medidas sobre como jsoncons
se compara a outras bibliotecas json
.
Conjuntos de testes JSONTestSuite e JSON_checker
Benchmarks de desempenho com texto e números inteiros
Benchmarks de desempenho com texto e duplicações
Comparação JSONPath mostra como jsoncons JsonPath se compara a outras implementações
Trabalhando com dados JSON
Trabalhando com dados CBOR
Para os exemplos abaixo você precisa incluir alguns arquivos de cabeçalho e inicializar uma string de dados 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 permite que você trabalhe com os dados de várias maneiras:
Como uma estrutura de dados semelhante a uma variante, basic_json
Como uma estrutura de dados C++ fortemente tipada que implementa json_type_traits
Com acesso no nível do cursor a um fluxo de eventos de análise
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 " ;
}
Saída:
(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 suporta a transformação de textos JSON em estruturas de dados C++. As funções decode_json e encode_json convertem strings ou fluxos de dados JSON em estruturas de dados C++ e vice-versa. Decodificar e codificar funcionam para todas as classes C++ que possuem json_type_traits definido. jsoncons já suporta muitos tipos na biblioteca padrão, e seus próprios tipos também serão suportados se você se especializar em json_type_traits
no namespace jsoncons
.
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 " ;
}
Saída:
(1)
Marilyn C, 0.9, 1514862245
(2)
{
"application": "hiking",
"reputons": [
{
"assertion": "advanced",
"generated": 1514862245,
"rated": "Marilyn C",
"rater": "HikingAsylum",
"rating": 0.9
}
]
}
Este exemplo faz uso das macros de conveniência JSONCONS_ENUM_TRAITS
, JSONCONS_N_CTOR_GETTER_TRAITS
e JSONCONS_ALL_CTOR_GETTER_TRAITS
para especializar o json_type_traits para o tipo de enum ns::hiking_experience
, a classe ns::hiking_reputon
(com alguns membros não obrigatórios) e a classe ns::hiking_reputation
( com todas as obrigatoriedades membros.) A macro JSONCONS_ENUM_TRAITS
gera o código a partir dos identificadores enum, e as macros JSONCONS_N_CTOR_GETTER_TRAITS
e JSONCONS_ALL_CTOR_GETTER_TRAITS
geram o código a partir das funções get e de um construtor. Essas declarações de macro devem ser colocadas fora de quaisquer blocos de namespace.
Veja exemplos para outras maneiras de especializar json_type_traits
.
Um aplicativo típico de análise pull processará repetidamente o evento current()
e chamará next()
para avançar para o próximo evento, até que done()
retorne 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 ;
}
}
}
Saída:
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
Você pode aplicar um filtro a um cursor usando a sintaxe de pipe (por exemplo, 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 ;
}
}
}
Saída:
Marilyn C
Para os exemplos abaixo você precisa incluir alguns arquivos de cabeçalho e inicializar um buffer de dados 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 permite que você trabalhe com dados CBOR de forma semelhante aos dados JSON:
Como uma estrutura de dados semelhante a uma variante, basic_json
Como uma estrutura de dados C++ fortemente tipada que implementa json_type_traits
Com acesso no nível do cursor a um fluxo de eventos de análise
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 " ;
}
Saída:
(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 " ;
}
Saída:
(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
Observe que ao decodificar o bigfloat e a fração decimal em um std::string
, perdemos as informações semânticas que a variante como estrutura de dados preservou com uma tag, portanto, a serialização de volta para CBOR produz uma string de texto.
Um aplicativo típico de análise pull processará repetidamente o evento current()
e chamará next()
para avançar para o próximo evento, até que done()
retorne 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 ;
}
}
}
Saída:
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)
Você pode aplicar um filtro a um cursor usando a sintaxe de pipe,
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 ;
}
}
}
Saída:
string_value: 0x3p-1 (bigfloat)
string_value: 1.23456789012345678901234567890 (bigdec)
jsoncons requer um compilador com suporte mínimo a C++ 11. Ele é testado em integração contínua no Github Actions e no Circleci. Os diagnósticos UndefinedBehaviorSanitizer (UBSan) são habilitados para compilações gcc e clang selecionadas. Desde a v0.151.0, está integrado ao Google OSS-fuzz, com cobertura para todos os analisadores e codificadores.
Compilador | Versão | Padrão | Arquitetura | Sistema operacional | Serviço CI |
---|---|---|---|---|---|
Estúdio visual | vs2019 | padrão | x86, x64 | Janelas 11 | Ações do GitHub |
vs2022 | padrão | x86, x64 | Janelas 11 | Ações do GitHub | |
Visual Studio - clang | vs2019 | padrão | x86, x64 | Janelas 11 | Ações do GitHub |
vs2022 | padrão | x86, x64 | Janelas 11 | Ações do GitHub | |
g++ | 6, 7, 8, 9, 10, 11, 12 | padrão | x64 | Ubuntu | círculo |
g++ | 12 | c++20 | x64 | Ubuntu | Ações do GitHub |
clangor | 3,9, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 | padrão | x64 | Ubuntu | círculo |
clangor | 14 | c++20 | x64 | Ubuntu | Ações do GitHub |
clang xcode | 11, 12, 13 | padrão | x64 | OS X 11 | Ações do GitHub |
clang xcode | 13, 14 | padrão | x64 | OS X 12 | Ações do GitHub |
CMake é uma ferramenta de construção multiplataforma que gera makefiles e soluções para o ambiente de compilação de sua escolha. No Windows você pode baixar um pacote do Windows Installer. No Linux geralmente está disponível como um pacote, por exemplo, no Ubuntu,
sudo apt-get install cmake
Depois que o cmake estiver instalado, você poderá criar e executar os testes de unidade no diretório jsoncons,
No Windows:
> mkdir build
> cd build
> cmake .. -DJSONCONS_BUILD_TESTS=On
> cmake --build .
> ctest -C Debug --output-on-failure
No UNIX:
$ mkdir build
$ cd build
$ cmake .. -DJSONCONS_BUILD_TESTS=On
$ cmake --build .
$ ctest --output-on-failure
jsoncons usa o analisador estático PVS-Studio, fornecido gratuitamente para projetos de código aberto.
Um grande obrigado à comunidade comp.lang.c++ pela ajuda com os detalhes de implementação.
A configuração binária dependente da plataforma jsoncons baseia-se no excelente tinycbor licenciado pelo MIT.
Obrigado a Milo Yip, autor do RapidJSON, por elevar a qualidade das bibliotecas JSON em todos os níveis, publicando os benchmarks e entrando em contato com este projeto (entre outros) para compartilhar os resultados.
A implementação jsoncons do algoritmo Grisu3 para impressão de números de ponto flutuante segue a implementação grisu3_59_56 licenciada pelo MIT de Florian Loitsch, com pequenas modificações.
A macro JSONCONS_ALL_MEMBER_TRAITS
segue a abordagem adotada por ThorsSerializer de Martin York
As implementações jsoncons de BSON decimal128 de e para string, e ObjectId de e para string, são baseadas na libbson licenciada do Apache 2.
Agradecimentos especiais aos nossos colaboradores