C/C++ json库

文章目录

  • 一、介绍
    • [1.1 json 介绍](#1.1 json 介绍)
  • [二、C/C++ json 库选型](#二、C/C++ json 库选型)
    • [2.1 选型范围](#2.1 选型范围)
    • [2.2 jsoncpp](#2.2 jsoncpp)
      • [2.2.2 jsoncpp 编译和交叉编译](#2.2.2 jsoncpp 编译和交叉编译)
    • [2.3 rapidjson](#2.3 rapidjson)
    • [2.4 nlohmann/json](#2.4 nlohmann/json)
    • [2.5 sonic-cpp](#2.5 sonic-cpp)
  • 五、常见问题
    • [5.1 jsoncpp 中关于浮点数的控制和中文显示问题](#5.1 jsoncpp 中关于浮点数的控制和中文显示问题)
    • [5.2 jsoncpp序列化double类型时精度损失问题的解决办法](#5.2 jsoncpp序列化double类型时精度损失问题的解决办法)

一、介绍

1.1 json 介绍

二、C/C++ json 库选型

2.1 选型范围

2.2 jsoncpp

  • 精度控制:precision
    • 15,16,17:原值会变
    • 0-14:原值不变的情况下,四舍五入

2.2.2 jsoncpp 编译和交叉编译

  • jsoncpp 的编译和交叉编译

  • 编译

    bash 复制代码
    mkdir build; cd build
    cmake -DCMAKE_BUILD_TYPE=Release \
    -DBUILD_SHARED_LIBS=ON \
    -DCMAKE_INSTALL_PREFIX=`pwd`/result \
    -DJSONCPP_WITH_TESTS=OFF \ ..
    make install -j4
  • 交叉编译

    bash 复制代码
    mkdir build; cd build
    cmake -DCMAKE_BUILD_TYPE=Release \
    -DBUILD_SHARED_LIBS=ON \
    -DCMAKE_INSTALL_PREFIX=`pwd`/result \
    -DJSONCPP_WITH_TESTS=OFF \ ..
    -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \ ..
    make install -j4
    
    # toolchain.cmake 定义交叉编译环境变量
    SET(CMAKE_SYSROOT "/opt/fslc-x11/2.4.4/sysroots/armv7at2hf-neon-fslc-linux-gnueabi")
    set(CMAKE_SYSTEM_NAME Linux)
    set(CMAKE_SYSTEM_PROCESSOR arm)
    set(TOOLS /opt/fslc-x11/2.4.4/sysroots/x86_64-fslcsdk-linux/usr/bin/arm-fslc-linux-gnueabi)
    set(CMAKE_C_COMPILER "${TOOLS}/arm-fslc-linux-gnueabi-gcc")
    set(CMAKE_CXX_COMPILER "${TOOLS}/arm-fslc-linux-gnueabi-g++")
    set(CMAKE_AR "${TOOLS}/arm-fslc-linux-gnueabi-ar")
  • include/json

  • lib/cmake/xxx.cmake

  • lib/pkgconfig/jsoncpp.pc

  • pkgconfig

    bash 复制代码
    mayue@PC-MAYUE:/mnt/d/hik/opensource/jsoncpp-1.9.5/build/result/lib$ cat pkgconfig/jsoncpp.pc
    prefix=/mnt/d/hik/opensource/jsoncpp-1.9.5/build/result
    exec_prefix=/mnt/d/hik/opensource/jsoncpp-1.9.5/build/result
    libdir=${exec_prefix}/lib
    includedir=${prefix}/include
    
    Name: jsoncpp
    Description: A C++ library for interacting with JSON
    Version: 1.9.5
    URL: https://github.com/open-source-parsers/jsoncpp
    Libs: -L${libdir} -ljsoncpp
    Cflags: -I${includedir}

指定连接静态库

bash 复制代码
g++ jsoncpp-test.cpp -I./include -L ./lib -l:libjsoncpp.a

2.3 rapidjson

2.4 nlohmann/json

2.5 sonic-cpp

五、常见问题

5.1 jsoncpp 中关于浮点数的控制和中文显示问题

5.2 jsoncpp序列化double类型时精度损失问题的解决办法

解决办法1:此法不需要改源码,使用StreamWriterBuilder进行序列化

cpp 复制代码
#include <json/json.h>
#include <json/writer.h>
#include <iostream>
#include <string>

void test_precision(int precision)
{
    Json::Value root;
    root["pi"] = 3.1415926;
    root["count"] = 43.32558674566;

    Json::StreamWriterBuilder builder;
    //设置精度 注意这个默认设置的是数字总长度 
    //如果想设置小数点后的位数需设置precisionType为decimal
    builder.settings_["precision"] = precision;
    //设置精度类型 只可设置2种字符串 significant精度位数为数字总长度(jsoncpp默认为此类型) decimal精度位数为小数点后的长度
    builder.settings_["precisionType"] = "decimal";
    // 设置输出为紧凑格式,不带换行和空格
    builder["commentStyle"] = "None";   // 防止输出注释,默认就是none
    builder["indentation"] = "";        // 空字符串表示不缩进
    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
    std::ostringstream oss;
    writer->write(root, &oss);
    std::string jsonText = oss.str();

    // 输出 JSON 字符串
    std::cout << "specified precision: " << precision << ", content:" << jsonText << std::endl;
}

int main() {

    for(int i=17; i>=0; i--)
    {
        test_precision(i);
    }
    return 0;
}
ba 复制代码
specified precision: 17, content:{"count":43.32558674565999723,"pi":3.14159260000000007}
specified precision: 16, content:{"count":43.3255867456599972,"pi":3.1415926000000001}
specified precision: 15, content:{"count":43.325586745659997,"pi":3.1415926}
specified precision: 14, content:{"count":43.32558674566,"pi":3.1415926}
specified precision: 13, content:{"count":43.32558674566,"pi":3.1415926}
specified precision: 12, content:{"count":43.32558674566,"pi":3.1415926}
specified precision: 11, content:{"count":43.32558674566,"pi":3.1415926}
specified precision: 10, content:{"count":43.3255867457,"pi":3.1415926}
specified precision: 9, content:{"count":43.325586746,"pi":3.1415926}
specified precision: 8, content:{"count":43.32558675,"pi":3.1415926}
specified precision: 7, content:{"count":43.3255867,"pi":3.1415926}
specified precision: 6, content:{"count":43.325587,"pi":3.141593}
specified precision: 5, content:{"count":43.32559,"pi":3.14159}
specified precision: 4, content:{"count":43.3256,"pi":3.1416}
specified precision: 3, content:{"count":43.326,"pi":3.142}
specified precision: 2, content:{"count":43.33,"pi":3.14}
specified precision: 1, content:{"count":43.3,"pi":3.1}
specified precision: 0, content:{"count":43,"pi":3}
  • 精度控制:precision

    • 15,16,17:原值会变、四舍五入
    • 0-14:原值不变的情况下,四舍五入
  • 特别注意:精度设置一定要大于你需求的精度位数,比如需要三位可以设置4位或5位,因为最后一位可能会不准(做了四舍五入)

不足之处:

StreamWriterBuilder序列化的字符串是可读形式的,就像上面的输出,是有换行和缩进的(转换效率会比FastWrite低),我的服务端代码里其实不需要转换json为可读的,更需要的是效率,所以还有下面一种方法改FasetWrite源码

解决办法2:此法需要改源码,使用FastWriter进行序列化

  • 注意:需升级jsoncpp到最新版本1.9.5版本

  • 修改源码(writer.h):FastWriter类新增2个成员变量(precision_和precisionType_)和成员函数(set_precision和set_precisionType)

    cpp 复制代码
    #if defined(_MSC_VER)
    #pragma warning(push)
    #pragma warning(disable : 4996) // Deriving from deprecated class
    #endif
    class JSON_API FastWriter
        : public Writer {
    public:
      FastWriter();
      ~FastWriter() override = default;
    
      void enableYAMLCompatibility();
    
      /** \brief Drop the "null" string from the writer's output for nullValues.
       * Strictly speaking, this is not valid JSON. But when the output is being
       * fed to a browser's JavaScript, it makes for smaller output and the
       * browser can handle the output just fine.
       */
      void dropNullPlaceholders();
    
      void omitEndingLineFeed();
    
    public: // overridden from Writer
      String write(const Value& root) override;
    
      //设置精度位数
      void set_precision(unsigned int precision) { precision_ = (precision > 17)?17:precision; };
      //设置精度类型 默认为数字总长
      //入参:isDecimal true表示类型为小数点后长度 false表示类型为数字总长
      void set_precisionType(bool isDecimal) { isDecimal ? (precisionType_ = PrecisionType::decimalPlaces) : (precisionType_ = PrecisionType::significantDigits); };
    
    
    private:
      void writeValue(const Value& value);
    
      String document_;
      bool yamlCompatibilityEnabled_{false};
      bool dropNullPlaceholders_{false};
      bool omitEndingLineFeed_{false};
      int precision_{ 17 };//精度位数 默认17位
      PrecisionType precisionType_{ PrecisionType::significantDigits };//精度类型 默认为数字总长
    };
    #if defined(_MSC_VER)
    #pragma warning(pop)
    #endif
  • 修改源码(json_writer.cpp):只修改了1行代码,FastWriter::writeValue函数中case realValue的处理中调用的valueToString新增了2个参数传递(源码中没有传递用的函数默认值,现在传了并且可以通过新增的2个成员函数进行设置)

    cpp 复制代码
    void FastWriter::writeValue(const Value& value) {
      switch (value.type()) {
      case nullValue:
        if (!dropNullPlaceholders_)
          document_ += "null";
        break;
      case intValue:
        document_ += valueToString(value.asLargestInt());
        break;
      case uintValue:
        document_ += valueToString(value.asLargestUInt());
        break;
      case realValue:
        //这里原先是document_ += valueToString(value.asDouble());
        //因为后2个参数没传,所以用的函数默认值即精度位数=17,精度类型=PrecisionType::significantDigits
        //修改后 现在会传这2个参数,具体值可以通过新增加的2个成员函数设置
        document_ += valueToString(value.asDouble(), precision_, precisionType_);
        break;
      case stringValue: {
        // Is NULL possible for value.string_? No.
        char const* str;
        char const* end;
        bool ok = value.getString(&str, &end);
        if (ok)
          document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
        break;
      }
      case booleanValue:
        document_ += valueToString(value.asBool());
        break;
      case arrayValue: {
        document_ += '[';
        ArrayIndex size = value.size();
        for (ArrayIndex index = 0; index < size; ++index) {
          if (index > 0)
            document_ += ',';
          writeValue(value[index]);
        }
        document_ += ']';
      } break;
      case objectValue: {
        Value::Members members(value.getMemberNames());
        document_ += '{';
        for (auto it = members.begin(); it != members.end(); ++it) {
          const String& name = *it;
          if (it != members.begin())
            document_ += ',';
          document_ += valueToQuotedStringN(name.data(), name.length());
          document_ += yamlCompatibilityEnabled_ ? ": " : ":";
          writeValue(value[name]);
        }
        document_ += '}';
      } break;
      }
    }
  • 需要重新编译库,然后demo验证,使用的代码:

    cpp 复制代码
    #include <json/json.h>
    #include <json/writer.h>
    #include <iostream>
    #include <string>
    
    void test_precision(int precision)
    {
        Json::Value obj;
        Json::FastWriter write;
        write.set_precision(precision);
        write.set_precisionType(true);
        obj["d"] = 2.1;
        obj["d2"] = 9.111;
        obj["d3"] = 9.123456789;
        auto rtn = write.write(obj);
        std::cout << "specified precision: " << precision << ", rtn:" << rtn << std::endl;
    }
    
    int main() {
        for(int i=17; i>=0; i--)
        {
            test_precision(i);
        }
        return 0;
    }
    bash 复制代码
    specified precision: 17, rtn:{"d":2.10000000000000009,"d2":9.11100000000000065,"d3":9.12345678900000046}
    
    specified precision: 16, rtn:{"d":2.1000000000000001,"d2":9.1110000000000007,"d3":9.1234567890000005}
    
    specified precision: 15, rtn:{"d":2.1,"d2":9.111000000000001,"d3":9.123456789}
    
    specified precision: 14, rtn:{"d":2.1,"d2":9.111,"d3":9.123456789}
    
    specified precision: 13, rtn:{"d":2.1,"d2":9.111,"d3":9.123456789}
    
    specified precision: 12, rtn:{"d":2.1,"d2":9.111,"d3":9.123456789}
    
    specified precision: 11, rtn:{"d":2.1,"d2":9.111,"d3":9.123456789}
    
    specified precision: 10, rtn:{"d":2.1,"d2":9.111,"d3":9.123456789}
    
    specified precision: 9, rtn:{"d":2.1,"d2":9.111,"d3":9.123456789}
    
    specified precision: 8, rtn:{"d":2.1,"d2":9.111,"d3":9.12345679}
    
    specified precision: 7, rtn:{"d":2.1,"d2":9.111,"d3":9.1234568}
    
    specified precision: 6, rtn:{"d":2.1,"d2":9.111,"d3":9.123457}
    
    specified precision: 5, rtn:{"d":2.1,"d2":9.111,"d3":9.12346}
    
    specified precision: 4, rtn:{"d":2.1,"d2":9.111,"d3":9.1235}
    
    specified precision: 3, rtn:{"d":2.1,"d2":9.111,"d3":9.123}
    
    specified precision: 2, rtn:{"d":2.1,"d2":9.11,"d3":9.12}
    
    specified precision: 1, rtn:{"d":2.1,"d2":9.1,"d3":9.1}
    
    specified precision: 0, rtn:{"d":2,"d2":9,"d3":9}

C++ json序列化库有哪些,哪个性能最好

C++中有多种JSON序列化库可供选择,包括但不限于以下几种:

  1. Rapidjson:这是一个非常流行的C++ JSON库,以其高性能著称,由腾讯团队开发 。

  2. nlohmann/json:这是一个现代的、基于C++11的JSON库,以其易用性和直观的接口而受到许多C++程序员的青睐 。

  3. sonic-cpp:由字节跳动STE团队和服务框架团队共同研发的高效JSON库,它利用CPU硬件特性和向量化编程,大幅提高了序列化和反序列化的性能。据报道,其解析性能是rapidjson的2.5倍 。

  4. JsonCpp:这是一个成熟的库,提供了丰富的功能来处理JSON数据。

  5. simdjson:这是一个使用SIMD指令集来加速解析的库,它提供了快速的解析性能,但不支持修改解析后的JSON结构 。

  6. yyjson:这是一个追求解析性能的库,使用链表结构,但在查找数据时性能较差 。

在这些库中,sonic-cpp 被报道为性能最好的库,它不仅提供了高效的解析性能,还解决了其他一些库的缺点,如simdjson和yyjson的问题,并支持高效的增删改查操作 。此外,sonic-cpp已经在字节跳动的多个核心业务中大规模使用,并通过了工程化的考验 。

如果您对性能有极高的要求,sonic-cpp可能是一个不错的选择。然而,选择哪个库还应考虑其他因素,如易用性、社区支持、库的活跃度和维护情况。

相关推荐
余额不足1213828 分钟前
C语言基础十六:枚举、c语言中文件的读写操作
linux·c语言·算法
ragnwang1 小时前
C++ Eigen常见的高级用法 [学习笔记]
c++·笔记·学习
罗伯特祥2 小时前
C调用gnuplot绘图的方法
c语言·plot
嵌入式科普3 小时前
嵌入式科普(24)从SPI和CAN通信重新理解“全双工”
c语言·stm32·can·spi·全双工·ra6m5
lqqjuly4 小时前
特殊的“Undefined Reference xxx“编译错误
c语言·c++
冰红茶兑滴水5 小时前
云备份项目--工具类编写
linux·c++
刘好念5 小时前
[OpenGL]使用 Compute Shader 实现矩阵点乘
c++·计算机图形学·opengl·glsl
2401_858286115 小时前
115.【C语言】数据结构之排序(希尔排序)
c语言·开发语言·数据结构·算法·排序算法
酒鬼猿6 小时前
C++进阶(二)--面向对象--继承
java·开发语言·c++
姚先生976 小时前
LeetCode 209. 长度最小的子数组 (C++实现)
c++·算法·leetcode