工作总结

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

» 文章出处: reille博客—http://velep.com , 如果没有特别声明,文章均为reille博客原创作品
» 郑重声明: 原创作品未经允许不得转载,如需转载请联系reille#qq.com(#换成@)
分享到:

 Leave a Reply

(必须)

(我会替您保密的)(必须)

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.

   
© 2012 velep.com | reille blog | 管理| 粤ICP备15065318号-2| 谷歌地图| 百度地图| Suffusion theme|Sayontan Sinha

无觅相关文章插件,快速提升流量