jsoncpp是一个优秀的开源C++ json库,被广泛应用。在寻找C/C++ JSON库时,没有选择CJSON,而是选择了jsoncpp,主要考虑我应用程序是C++编写,如果使用CJSON的话,可能需要重新封装一层。
就我的应用而言,jsoncpp和CJSON都有一个共同问题:官方代码不支持指定小数位数。jsoncpp还有另一个问题:它默认按字母排序输出json。
一、按插入顺序输出json
我的应用需要按插入顺序输出json,所以不得不解决这个问题。
jsoncpp库采用map关联容器存储节点数据,map的特点是插入和查找时采用了红黑树算法,这决定了jsoncpp库并不是按插入顺序输出节点数据的。
但按该文章方法,总是实现不了按插入顺序输出。期间考虑了使用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(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;
}
三、代码下载
» 文章出处:
reille博客—http://velep.com
, 如果没有特别声明,文章均为reille博客原创作品
» 郑重声明:
原创作品未经允许不得转载,如需转载请联系reille#qq.com(#换成@)
分享到: