IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    jsoncpp按插入顺序排序和支持指定小数位数

    reille发表于 2019-06-02 14:59:42
    love 0

    jsoncpp是一个优秀的开源C++ json库,被广泛应用。在寻找C/C++ JSON库时,没有选择CJSON,而是选择了jsoncpp,主要考虑我应用程序是C++编写,如果使用CJSON的话,可能需要重新封装一层。

    就我的应用而言,jsoncpp和CJSON都有一个共同问题:官方代码不支持指定小数位数。jsoncpp还有另一个问题:它默认按字母排序输出json。

    一、按插入顺序输出json

    我的应用需要按插入顺序输出json,所以不得不解决这个问题。

    jsoncpp库采用map关联容器存储节点数据,map的特点是插入和查找时采用了红黑树算法,这决定了jsoncpp库并不是按插入顺序输出节点数据的。

    网上有参考文章:https://blog.csdn.net/sdcxyz/article/details/11906453

    但按该文章方法,总是实现不了按插入顺序输出。期间考虑了使用unordered_map、开源的fifo_map(https://github.com/nlohmann)来替换map,但总是有一些让人难懂的编译错误。所以最终放弃。

    后面想了一个方法:插入数据时,记录插入索引;输出数据时,按插入索引输出。

    首先,在class Value 中增加公有变量:

    1559485397(1)

     

     

    其中,m_precision初始化为17,m_insertIdx可不用初始化。

    其次,找到插入数据的地方。在jsoncpp.cpp中搜索关键字:value_.map_->insert。有3个地方,以其中resolveReference为例,修改如下:

    // @param key is not null-terminated.
    Value& Value::resolveReference(char const* key, char const* cend)
    {
      JSON_ASSERT_MESSAGE(
          type_ == nullValue || type_ == objectValue,
          "in Json::Value::resolveReference(key, end): requires objectValue");
      if (type_ == nullValue)
        *this = Value(objectValue);
      CZString actualKey(
          key, static_cast<unsigned>(cend-key), CZString::duplicateOnCopy);
      ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
      if (it != value_.map_->end() && (*it).first == actualKey)
        return (*it).second;
    
    #if 1// 记录节点插入的索引,用于按插入顺序输出 added by gyr 2019.06.02
      const unsigned int idx = value_.map_->size();
    #endif
    
      ObjectValues::value_type defaultValue(actualKey, nullRef);
      it = value_.map_->insert(it, defaultValue);
      Value& value = (*it).second;
    #if 1// 记录节点插入的索引,用于按插入顺序输出 added by gyr 2019.06.02
      value.m_insertIdx = idx;
    #endif
      return value;
    }

    最后,找到输出数据的地方:Value::Members Value::getMemberNames()。修改如下:

    Value::Members Value::getMemberNames() const {
      JSON_ASSERT_MESSAGE(
          type_ == nullValue || type_ == objectValue,
          "in Json::Value::getMemberNames(), value must be objectValue");
      if (type_ == nullValue)
        return Value::Members();
      Members members;
      members.reserve(value_.map_->size());
      ObjectValues::const_iterator it = value_.map_->begin();
      ObjectValues::const_iterator itEnd = value_.map_->end();
    #if 1 // 按插入顺序输出 added by gyr 2019.06.02
      unsigned int idx = 0;
      idx = 0;// 避免不使用时出现编译警告
    #endif
      for (; it != itEnd; ++it) {
    
    #if 1 // 按插入顺序输出 added by gyr 2019.06.02
    	  ObjectValues::const_iterator it1 = value_.map_->begin();
    	  for (; it1 != itEnd; ++it1) {
    		  if (it1->second.m_insertIdx == idx) {
    			    members.push_back(std::string((*it1).first.data(),
    			                                  (*it1).first.length()));
    			    break;
    		  }
    	  }
    
    	  idx++;
    
    #else
        members.push_back(std::string((*it).first.data(),
                                      (*it).first.length()));
    #endif
      }
      return members;
    }

    二、支持指定小数位数

    对于实数类型,jsoncpp默认按%.17g进行输出,详见如下函数实现:

    std::string valueToString(double value, bool useSpecialFloats, unsigned int precision)

    该函数的调用者,precision固定为17。这也是上面新增公有成员m_precision初始化为17的缘故。

    我的应用没超过6位小数,%.17g这种用法不是很明确,所以对上面函数进行了修改,如下:

    std::string valueToString(double value, bool useSpecialFloats, unsigned int precision) {
      // Allocate a buffer that is more than large enough to store the 16 digits of
      // precision requested below.
      char buffer[32];
      int len = -1;
    
      // added by gyr 2019.03.27
      // precision默认17位小数,对于小于6位小数进行特殊处理
      // printf %g用法不是很明确,所以对于超过6位小数的处理按原始方式
      if (precision <= 6)
      {
    	  switch (precision)
    	  {
    	  case 0: sprintf(buffer, "%.0f", value); break;
    	  case 1: sprintf(buffer, "%.1f", value); break;
    	  case 2: sprintf(buffer, "%.2f", value); break;
    	  case 3: sprintf(buffer, "%.3f", value); break;
    	  case 4: sprintf(buffer, "%.4f", value); break;
    	  case 5: sprintf(buffer, "%.5f", value); break;
    	  case 6: sprintf(buffer, "%.6f", value); break;
    	  }
    
    	  return buffer;
      }
    
      char formatString[6];
      sprintf(formatString, "%%.%dg", precision);
    
      // Print into the buffer. We need not request the alternative representation
      // that always has a decimal point because JSON doesn't distingish the
      // concepts of reals and integers.
      if (isfinite(value)) {
        len = snprintf(buffer, sizeof(buffer), formatString, value);
      } else {
        // IEEE standard states that NaN values will not compare to themselves
        if (value != value) {
          len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
        } else if (value < 0) {
          len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
        } else {
          len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999");
        }
        // For those, we do not need to call fixNumLoc, but it is fast.
      }
      assert(len >= 0);
      fixNumericLocale(buffer, buffer + len);
      return buffer;
    }

    三、代码下载

    修改后的代码下载链接:http://velep.com/downloads?did=22

    » 本文地址: http://velep.com/archives/1444.html
    » 文章出处: reille博客—http://velep.com , 如果没有特别声明,文章均为reille博客原创作品
    » 郑重声明: 原创作品未经允许不得转载,如需转载请联系reille#qq.com(#换成@)
    分享到:
    推荐阅读相关文章:
    • 关于FLT_EPSILON、DBL_EPSILON、LDBL_EPSILON
    • ymodem源码(基于C语言实现)
    • terminate called after throwing an instance of ‘std::length_error’ what(): basic_string::_S_create
    • error: ‘__locale_t’ has not been declared
    • 编译错误:error: stray ‘\357’ in program
    • linux backtrace()详细使用说明,分析Segmentation fault
    • linux下统计代码执行时间
    • 谈谈程序中BSS段大小问题


沪ICP备19023445号-2号
友情链接