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 中增加公有变量:
其中,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
推荐阅读相关文章:
- 关于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段大小问题