Json文件解析(上)

Json文件解析(上)

代码地址:https://github.com/nlohmann/json

自述文件  alt=GitHub赞助商 data-canonical-src="https://img.shields.io/badge/GitHub-Sponsors-ff69b4" v:shapes="_x0000_i1025">

设计目标

那里有无数的JSON库,每个库甚至都有其存在的理由。班有以下设计目标:

  • 直观的语法。在Python等语言中,JSON感觉就像是一流的数据类型。使用了现代C ++的所有操作符魔术,以在代码中实现相同的感觉。查看以下示例,将了解意思。
  • 微不足道的整合。整个代码包含一个头文件json.hpp。没有库,没有子项目,没有依赖项,没有复杂的构建系统。该类用香草C ++ 11编写。总而言之,一切都不需要调整编译器标志或项目设置。
  • 认真测试。项目经过严格的单元测试,涵盖了100%的代码,包括所有异常行为。此外,使用ValgrindClang网络检查是否有内存泄漏。Google OSS-Fuzz还针对所有解析器24/7运行模糊测试,到目前为止,有效执行了数十亿次测试。为了保持高质量,该项目遵循核心基础设施计划(CII)最佳实践

其方面对而言并不那么重要:

  • 存储效率。每个JSON对象的开销为一个指针(联合的最大大小)和一个枚举元素(1个字节)。默认归纳使用以下C ++数据类型:std::string用于字符串int64_t,uint64_t或double数字,std::map对象,std::vector数组和bool布尔值。但是,可以根据basic_json需要对通用类进行模板化。
  • 速度。当然,那里有更快的JSON库。但是,如果目标是通过使用单个标头添加JSON支持来加快开发速度,那么该库就是理想之选。如果知道如何使用std::vector或std::map,那么已经设置好了。

积分

json.hpp此处single_include/nlohmann或发布的单个必需文件。需要添加

#包括 < nlohmann / json.hpp >

//为了方便

使用 json = nlohmann :: json;

到要处理JSON的文件,并设置必要的开关以启用C ++ 11(例如,-std=c++11对于GCC和Clang)。

可以进一步使用file include/nlohmann/json_fwd.hpp进行前向声明。json_fwd.hpp的安装(作为cmake安装步骤的一部分)可以通过设置来实现-DJSON_MultipleHeaders=ON。

CMake

也可以nlohmann_json::nlohmann_json在CMake中使用接口目标。此目标填充适当的使用要求,INTERFACE_INCLUDE_DIRECTORIES以指向适当的包含目录和INTERFACE_COMPILE_FEATURES必要的C ++ 11标志。

外部

要从CMake项目中使用此库,可以直接从中找到,find_package()并使用生成的程序包配置中的命名空间导入目标:

#CMakeLists.txt

find_package(nlohmann_json 3.2.0 REQUIRED)

...

add_library(foo ...)

...

target_link_libraries(foo PRIVATE nlohmann_json :: nlohmann_json)

软件包配置文件nlohmann_jsonConfig.cmake可以在安装树中使用,也可以在构建树之外直接使用。

嵌入式的

要将库直接嵌入到现有CMake项目中,请将整个源代码树放置在子目录中,然后调用add_subdirectory()CMakeLists.txt文件:

#通常不那么关心第三方库的测试是

#从自己的项目的代码运行。

设置(JSON_BuildTests OFF  CACHE INTERNAL “”)

 

#如果仅在私有源文件中包含此第三方,则

#在安装主项目时不需要安装。

#集(JSON_Install OFF CACHE INTERNAL “”)

 

#请勿使用include(nlohmann_json / CMakeLists.txt),因为会附带

#会导致构建中断的意外后果。通常

#鼓励(虽然不一定有据可查这样)使用

#包括(...),用于其项目的CMake反正拉动。

add_sub目录(nlohmann_json)

...

add_library(foo ...)

...

target_link_libraries(foo PRIVATE nlohmann_json :: nlohmann_json)

嵌入式(FetchContent

从CMake v3.11开始, FetchContent可以作为配置类型的依赖项自动下载资源库。

例:

包括(FetchContent)

 

FetchContent_Declare(json

  GIT_REPOSITORY https://github.com/nlohmann/json.git

  GIT_TAG v3.7.3)

 

FetchContent_GetProperties(json)

如果(NOT json_POPULATED)

  FetchContent_Populate(json)

  add_subdirectory($ {json_SOURCE_DIR}  $ {json_BINARY_DIR}  EXCLUDE_FROM_ALL)

 endif()

 

target_link_libraries(foo PRIVATE nlohmann_json :: nlohmann_json)

注意:仓库https://github.com/nlohmann/json的下载量很大。包含用于基准测试的所有数据集。可能需要依赖较小的存储库。例如,可能想用https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent替换上面的URL

同时支持

为了允许项目支持外部提供的或嵌入式JSON库,可以使用类似于以下内容的模式:

# Top level CMakeLists.txt
project(FOO)
...
option(FOO_USE_EXTERNAL_JSON "Use an external JSON library" OFF)
...
add_subdirectory(thirdparty)
...
add_library(foo ...)
...
# Note that the namespaced target will always be available regardless of the
# import method
target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)
# thirdparty/CMakeLists.txt
...
if(FOO_USE_EXTERNAL_JSON)
  find_package(nlohmann_json 3.2.0 REQUIRED)
else()
  set(JSON_BuildTests OFF CACHE INTERNAL "")
  add_subdirectory(nlohmann_json)
endif()
...

thirdparty/nlohmann_json 然后是此源树的完整副本。

包管理Package Managers

If you are using OS X and Homebrew, just type brew tap nlohmann/json and brew install nlohmann-json and you're set. If you want the bleeding edge rather than the latest release, use brew install nlohmann-json --HEAD.

If you are using the Meson Build System, add this source tree as a meson subproject. You may also use the include.zip published in this project's Releases to reduce the size of the vendored source tree. Alternatively, you can get a wrap file by downloading it from Meson WrapDB, or simply use meson wrap install nlohmann_json. Please see the meson project for any issues regarding the packaging.

The provided meson.build can also be used as an alternative to cmake for installing nlohmann_json system-wide in which case a pkg-config file is installed. To use it, simply have your build system require the nlohmann_json pkg-config dependency. In Meson, it is preferred to use the dependency() object with a subproject fallback, rather than using the subproject directly.

If you are using Conan to manage your dependencies, merely add nlohmann_json/x.y.z to your conanfile's requires, where x.y.z is the release version you want to use. Please file issues here if you experience problems with the packages.

If you are using Spack to manage your dependencies, you can use the nlohmann-json package. Please see the spack project for any issues regarding the packaging.

If you are using hunter on your project for external dependencies, then you can use the nlohmann_json package. Please see the hunter project for any issues regarding the packaging.

If you are using Buckaroo, you can install this library's module with buckaroo add github.com/buckaroo-pm/nlohmann-json. Please file issues here. There is a demo repo here.

If you are using vcpkg on your project for external dependencies, then you can use the nlohmann-json package. Please see the vcpkg project for any issues regarding the packaging.

If you are using cget, you can install the latest development version with cget install nlohmann/json. A specific version can be installed with cget install nlohmann/json@v3.1.0. Also, the multiple header version can be installed by adding the -DJSON_MultipleHeaders=ON flag (i.e., cget install nlohmann/json -DJSON_MultipleHeaders=ON).

If you are using CocoaPods, you can use the library by adding pod "nlohmann_json", '~>3.1.2' to your podfile (see an example). Please file issues here.

If you are using NuGet, you can use the package nlohmann.json. Please check this extensive description on how to use the package. Please files issues here.

If you are using conda, you can use the package nlohmann_json from conda-forge executing conda install -c conda-forge nlohmann_json. Please file issues here.

If you are using MSYS2, your can use the mingw-w64-nlohmann-json package, just type pacman -S mingw-w64-i686-nlohmann-json or pacman -S mingw-w64-x86_64-nlohmann-json for installation. Please file issues here if you experience problems with the packages.

If you are using build2, you can use the nlohmann-json package from the public repository https://cppget.org or directly from the package's sources repository. In your project's manifest file, just add depends: nlohmann-json(probably with some version constraints). If you are not familiar with using dependencies in build2please read this introduction. Please file issues here if you experience problems with the packages.

If you are using wsjcpp, you can use the command wsjcpp install "https://github.com/nlohmann/json:develop" to get the latest version. Note you can change the branch ":develop" to an existing tag or another branch。

包配置

如果使用的是裸Makefile,则可以使用pkg-config生成指向库安装位置的include标志:

pkg-config nlohmann_json --cflags

Meson构建系统的用户还可以使用系统范围的库,该库可以通过pkg-config以下方式找到:

json = dependency('nlohmann_json', required: true)

例子

除了下面的示例,可能想查看文档,其中每个函数都包含一个单独的代码示例(例如,检出emplace())。所有示例文件都可以单独编译和执行(例如,文件emplace.cpp)。

JSON作为一流的数据类型

以下是一些示例,可让了解如何使用该类。

假设要创建JSON对象

{
  "pi": 3.141,
  "happy": true,
  "name": "Niels",
  "nothing": null,
  "answer": {
    "everything": 42
  },
  "list": [1, 0, 2],
  "object": {
    "currency": "USD",
    "value": 42.99
  }
}

使用该库,可以编写:

//创建一个空结构(空)

json j;

 

//添加一个存储为double的数字(注意j到对象的隐式转换)

j [ “ pi ” ] = 3.141 ;

 

//添加一个存储为bool

j [ “ happy ” ] = true的布尔值;

 

//添加一个存储为std :: string

j [ “ name ” ] = “ Niels ”的字符串;

 

//通过传递nullptr

j [ “ nothing ” ] = nullptr来添加另一个null对象;

 

//在对象

j [ “ answer ” ] [ “ everything ” ] = 42 内添加一个对象;

 

//添加存储为标准::向量(使用初始化列表)的阵列

f] [ “列表” ] = { 1, 0, 2 };

 

//添加另一个对象(使用成对的初始化列表)

j [ “ object ” ] = {{ “ currency ”, “ USD ” },{ “ value ”, 42.99 }};

 

//相反,也可以编写(看起来与上面的JSON非常相似)

json j2 = {

  { “ pi ”,3.141 },

  { “ happy ”,true },

  { “ name ”,“ Niels ” },

  { “没有”,nullptr },

  { “答案”,{

    { “一切”,42 }

  }},

  { “列表”,{ 1,0,2 }},

  { “对象”,{

    { “货币”,“美元” },

    { “值”,42.99 }

  }}

};

请注意,在所有这些情况下,都无需“告诉”编译器要使用的JSON值类型。如果想明确表达或表达一些极端情况,这些函数json::array()json::object()将有助于:

//表达空数组[]的方法

json empty_array_explicit = json :: array();

 

//表示空对象的方法{}

json empty_object_implicit = json({});

json empty_object_explicit = json :: object();

 

//一种方式来表达的键/值对的_array_ [[ “货币”, “USD”],[ “值”,42.99]]

JSON array_not_object = JSON ::阵列({{ “货币”, “ USD ” } ,{ “ value ”, 42.99 }});

序列化/反序列化

/从字符串

可以通过附加_json到字符串文字来创建JSON值(反序列化):

//从字符串文字

json创建对象 j = “ { \” happy \“:true,\” pi \“: 3.141 } ” _json;

 

//甚至使用原始字符串文字

自动 j2 = R“(

   {

    ” happy“:true,

    ” pi“:

 3.141   }

)” _json;

请注意,不附加_json后缀,传递的字符串文字不被解析,而仅用作JSON字符串值。也就是说,json j = "{ \"happy\": true, \"pi\": 3.141 }"将只存储字符串"{ "happy": true, "pi": 3.141 }"而不是解析实际对象。

上面的示例也可以使用来明确表示json::parse()

//明确解析

自动 j3 = json :: parse( “ { \” happy \“:true,\” pi \“:3.141} ”);

还可以获取JSON值的字符串表示形式(序列化):

//明确转换为字符串

std :: string s = j.dump();    // {“ happy”:true,“ pi”:3.141}

 

//通过漂亮的打印进行序列化

//传递空格以缩进

std :: cout << j.dump( 4)<< std :: endl;

// {

//      “ happy”:true,

//      “ pi”:3.141

// }

注意序列化和赋值之间的区别:

//在JSON值中存储一个字符串

json j_string = “这是一个字符串” ;

 

//检索字符串值

auto cpp_string = j_string.get <std :: string>();

//检索字符串值(变量已经存在时的替代方法)

std :: string cpp_string2;

j_string.get_to(cpp_string2);

 

//检索序列化的值(显式JSON序列化)

std :: string serialized_string = j_string.dump();

 

//输出原始字符串

std :: cout << cpp_string << “ == ” << cpp_string2 << “ == ” << j_string.get <std :: string>()<< ' \ n ' ;

//输出序列化值

std :: cout << j_string << “ == ” << serialized_string << std :: endl;

.dump()始终返回序列化的值,并.get<std::string>()返回原始存储的字符串值。

请注意,该库仅支持UTF-8。当在库中存储具有不同编码的字符串时,dump()除非json::error_handler_t::replace或json::error_handler_t::ignore用作错误处理程序,否则调用可能会引发异常。

往返流(例如文件,字符串流)

还可以使用流来序列化和反序列化:

//从标准输入反序列化

json j;

std :: cin >> j;

 

//序列化为标准输出

std :: cout << j;

 

// setw操纵器过载以设置漂亮打印的缩进

std :: cout << std :: setw( 4)<< j << std :: endl;

这些运算符可用于std::istream或的任何子类std::ostream。这是文件的相同示例:

//读取JSON文件

std :: ifstream i( “ file.json ”);

json j;

>> j;

 

//将经过修饰的JSON写入另一个文件

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());

可以将迭代器保留在[begin,end)范围内:

std :: vector <std :: uint8_t > v = { ' t ',' r ',' u ',' e ' };

json j = json :: parse(v);

自定义数据源

由于parse函数接受任意迭代器范围,因此可以通过实现此LegacyInputIterator概念来提供自己的数据源。

struct  MyContainer {

   void  advance();

  const  char&get_current();

};

 

struct  MyIterator {

     使用 difference_type = std :: ptrdiff_t ;

    使用 value_type = char ;

    使用指针= const  char *;

    使用 reference = const  char&;

    使用 iterator_category = std :: input_iterator_tag;

 

    MyIterator&运算符 ++(){

        MyContainer。前进();

        返回 * 这个 ;

    }

 

    布尔 运算符!=(const MyIterator&rhs)const {

         return rhs。目标!=目标;

    }

 

    引用运算符 *()const {

         返回目标。get_current();

    }

 

    MyContainer * target = nullptr ;

};

 

MyIterator 开始(MyContainer&tgt){

     return MyIterator {&tgt};

}

 

MyIterator 结束(const MyContainer&){

     return {};

}

 

无效 foo(){

    MyContainer c;

    json j = json :: parse(c);

}

SAX接口

该库使用具有以下功能的类SAX接口:

//当解析为

null 时调用bool  null();

 

//在解析布尔值时调用;值传递给

布尔布尔 值(布尔值);

 

//在解析有符号或无符号整数时调用;值传递给

bool  number_integer( number_integer_t val);

bool  number_unsigned( number_unsigned_t val);

 

//在解析浮点数时调用;值和原始字符串传递给

bool  number_float( number_float_t val, const  string_t&s);

 

//解析字符串时调用;值被传递并且可以安全地移开

布尔 字符串( string_t&val);

 

//分别在对象或数组开始或结束时调用。传递的元素数量(如果不知道,则传递-1)

bool  start_object(std :: size_t元素);

bool  end_object();

bool  start_array(std :: size_t元素);

bool  end_array();

//在解析对象键时调用;值被传递并且可以安全地移开

bool  键( string_t&val);

 

//发生解析错误时调用;字节位置,最后一个标记和一个异常被传递给

bool  parse_error(std :: size_t position, const std :: string&last_token, const detail :: exception&ex);

每个函数的返回值确定是否应该进行解析。

要实现自己的SAX处理程序,请按照下列步骤操作:

  1. 在一个类中实现SAX接口。可以将类nlohmann::json_sax<json>用作基类,但也可以使用实现了上述功能并且将其公开的任何类。
  2. 创建SAX接口类的对象,例如my_sax。
  3. 致电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),请参见file json_sax.hpp

类似STL的访问

设计了JSON类,使其表现得像STL容器一样。实际上,满足了ReversibleContainer要求。

//使用push_back创建一个数组

json j;

j.push_back(“ foo ”);

j.push_back(1);

j.push_back(true);

 

//还使用emplace_back

j.emplace_back( 1.78);

 

//

为(json :: iterator it = j.begin(); it!= j.end(); ++ it)迭代数组 {

  std :: cout << * it << ' \ n ' ;

}

 

//基于范围的

for( auto&element:j){

  std :: cout <<元素<< ' \ n ' ;

}

 

// getter / setter

const  auto tmp = j [ 0 ] .get <std :: string>();

j [ 1 ] = 42 ;

bool foo = j.at(2);

 

//比较

j == “ [ \” foo \“,42,true] ” _json;  //正确

 

//其东西

j.size();     // 3个条目

j.empty();    // false

j.type();     // json :: value_t :: array

j.clear();    //数组再次为空

 

//便利类型检查器

j.is_null();

j.is_boolean();

j.is_number();

j.is_object();

j.is_array();

j.is_string();

 

//创建一个对象

json o;

o [ “ foo ” ] = 23 ;

o [ “ bar ” ] = false ;

o [ “ baz ” ] = 3.141 ;

 

//还可以使用

emplace o.emplace( “ weather ”, “ sunny ”);

 

// //对象专用的迭代器成员函数,

用于(json :: iterator it = o.begin(); it!= o.end(); ++ it){

  std :: cout <<。key()<< “:” <<。value()<< “ \ n ” ;

}

 

//与

for( auto&el:o.items()){

  std :: cout << el。key()<< “:” << << value()<< “ \ n ” ;

}

 

//即使结构化绑定(C ++ 17)更容易

为(自动&[键,值]:o.items()){

  std :: cout <<键<< “:” <<值<< “ \ n ” ;

}

 

//找到一个条目

,如果(o.contains( “富”)){

   //存在与键“foo”的条目

}

 

//或者通过find和一个迭代器

if(o.find( “ foo ”)!= o.end()){

   //有一个键为“ foo”的条目

}

 

//或更简单地使用count()

int foo_present = o.count( “ foo ”); // 1

int fob_present = o.count( “ fob ”); // 0

 

//删除条目

o.erase( “ foo ”);

 

posted @ 2020-07-14 18:57  吴建明wujianming  阅读(970)  评论(0编辑  收藏  举报