Merge pull request #19 from siyuhong/master
fix double value save as scientific notation
This commit is contained in:
commit
c73ed6c97a
3
main.cpp
3
main.cpp
@ -59,5 +59,8 @@ int main(int argc, char *argv[])
|
||||
model->loadJson(QByteArray::fromStdString(json));
|
||||
view->show();
|
||||
|
||||
QByteArray mjson = model->json();
|
||||
qDebug() << mjson;
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
|
209
qjsonmodel.cpp
209
qjsonmodel.cpp
@ -140,6 +140,85 @@ QJsonTreeItem* QJsonTreeItem::load(const QJsonValue& value, QJsonTreeItem* paren
|
||||
|
||||
//=========================================================================
|
||||
|
||||
inline uchar hexdig(uint u)
|
||||
{
|
||||
return (u < 0xa ? '0' + u : 'a' + u - 0xa);
|
||||
}
|
||||
|
||||
QByteArray escapedString(const QString &s)
|
||||
{
|
||||
QByteArray ba(s.length(), Qt::Uninitialized);
|
||||
uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(ba.constData()));
|
||||
const uchar *ba_end = cursor + ba.length();
|
||||
const ushort *src = reinterpret_cast<const ushort *>(s.constBegin());
|
||||
const ushort *const end = reinterpret_cast<const ushort *>(s.constEnd());
|
||||
while (src != end)
|
||||
{
|
||||
if (cursor >= ba_end - 6)
|
||||
{
|
||||
// ensure we have enough space
|
||||
int pos = cursor - (const uchar *)ba.constData();
|
||||
ba.resize(ba.size() * 2);
|
||||
cursor = (uchar *)ba.data() + pos;
|
||||
ba_end = (const uchar *)ba.constData() + ba.length();
|
||||
}
|
||||
uint u = *src++;
|
||||
if (u < 0x80)
|
||||
{
|
||||
if (u < 0x20 || u == 0x22 || u == 0x5c)
|
||||
{
|
||||
*cursor++ = '\\';
|
||||
switch (u)
|
||||
{
|
||||
case 0x22:
|
||||
*cursor++ = '"';
|
||||
break;
|
||||
case 0x5c:
|
||||
*cursor++ = '\\';
|
||||
break;
|
||||
case 0x8:
|
||||
*cursor++ = 'b';
|
||||
break;
|
||||
case 0xc:
|
||||
*cursor++ = 'f';
|
||||
break;
|
||||
case 0xa:
|
||||
*cursor++ = 'n';
|
||||
break;
|
||||
case 0xd:
|
||||
*cursor++ = 'r';
|
||||
break;
|
||||
case 0x9:
|
||||
*cursor++ = 't';
|
||||
break;
|
||||
default:
|
||||
*cursor++ = 'u';
|
||||
*cursor++ = '0';
|
||||
*cursor++ = '0';
|
||||
*cursor++ = hexdig(u >> 4);
|
||||
*cursor++ = hexdig(u & 0xf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*cursor++ = (uchar)u;
|
||||
}
|
||||
}
|
||||
else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, cursor, src, end) < 0)
|
||||
{
|
||||
// failed to get valid utf8 use JSON escape sequence
|
||||
*cursor++ = '\\';
|
||||
*cursor++ = 'u';
|
||||
*cursor++ = hexdig(u >> 12 & 0x0f);
|
||||
*cursor++ = hexdig(u >> 8 & 0x0f);
|
||||
*cursor++ = hexdig(u >> 4 & 0x0f);
|
||||
*cursor++ = hexdig(u & 0x0f);
|
||||
}
|
||||
}
|
||||
ba.resize(cursor - (const uchar *)ba.constData());
|
||||
return ba;
|
||||
}
|
||||
|
||||
QJsonModel::QJsonModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, mRootItem{new QJsonTreeItem}
|
||||
@ -348,19 +427,129 @@ Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const
|
||||
}
|
||||
}
|
||||
|
||||
QJsonDocument QJsonModel::json() const
|
||||
QByteArray QJsonModel::json()
|
||||
{
|
||||
|
||||
auto v = genJson(mRootItem);
|
||||
QJsonDocument doc;
|
||||
|
||||
if (v.isObject()) {
|
||||
doc = QJsonDocument(v.toObject());
|
||||
} else {
|
||||
doc = QJsonDocument(v.toArray());
|
||||
auto jsonValue = genJson(mRootItem);
|
||||
QByteArray json;
|
||||
if (jsonValue.isNull())
|
||||
{
|
||||
return json;
|
||||
}
|
||||
if (jsonValue.isArray())
|
||||
{
|
||||
arrayToJson(jsonValue.toArray(), json, 0, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
objectToJson(jsonValue.toObject(), json, 0, false);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
return doc;
|
||||
void QJsonModel::objectToJson(QJsonObject jsonObject, QByteArray &json, int indent, bool compact)
|
||||
{
|
||||
json += compact ? "{" : "{\n";
|
||||
objectContentToJson(jsonObject, json, indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += compact ? "}" : "}\n";
|
||||
}
|
||||
void QJsonModel::arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent, bool compact)
|
||||
{
|
||||
json += compact ? "[" : "[\n";
|
||||
arrayContentToJson(jsonArray, json, indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += compact ? "]" : "]\n";
|
||||
}
|
||||
|
||||
void QJsonModel::arrayContentToJson(QJsonArray jsonArray, QByteArray &json, int indent, bool compact)
|
||||
{
|
||||
if (jsonArray.size() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
QByteArray indentString(4 * indent, ' ');
|
||||
int i = 0;
|
||||
while (1)
|
||||
{
|
||||
json += indentString;
|
||||
valueToJson(jsonArray.at(i), json, indent, compact);
|
||||
if (++i == jsonArray.size())
|
||||
{
|
||||
if (!compact)
|
||||
json += '\n';
|
||||
break;
|
||||
}
|
||||
json += compact ? "," : ",\n";
|
||||
}
|
||||
}
|
||||
void QJsonModel::objectContentToJson(QJsonObject jsonObject, QByteArray &json, int indent, bool compact)
|
||||
{
|
||||
if (jsonObject.size() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
QByteArray indentString(4 * indent, ' ');
|
||||
int i = 0;
|
||||
while (1)
|
||||
{
|
||||
QString key = jsonObject.keys().at(i);
|
||||
json += indentString;
|
||||
json += '"';
|
||||
json += escapedString(key);
|
||||
json += compact ? "\":" : "\": ";
|
||||
valueToJson(jsonObject.value(key), json, indent, compact);
|
||||
if (++i == jsonObject.size())
|
||||
{
|
||||
if (!compact)
|
||||
json += '\n';
|
||||
break;
|
||||
}
|
||||
json += compact ? "," : ",\n";
|
||||
}
|
||||
}
|
||||
|
||||
void QJsonModel::valueToJson(QJsonValue jsonValue, QByteArray &json, int indent, bool compact)
|
||||
{
|
||||
QJsonValue::Type type = jsonValue.type();
|
||||
switch (type)
|
||||
{
|
||||
case QJsonValue::Bool:
|
||||
json += jsonValue.toBool() ? "true" : "false";
|
||||
break;
|
||||
case QJsonValue::Double:
|
||||
{
|
||||
const double d = jsonValue.toDouble();
|
||||
if (qIsFinite(d))
|
||||
{
|
||||
json += QByteArray::number(d, 'f', QLocale::FloatingPointShortest);
|
||||
}
|
||||
else
|
||||
{
|
||||
json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QJsonValue::String:
|
||||
json += '"';
|
||||
json += escapedString(jsonValue.toString());
|
||||
json += '"';
|
||||
break;
|
||||
case QJsonValue::Array:
|
||||
json += compact ? "[" : "[\n";
|
||||
arrayContentToJson(jsonValue.toArray(), json, indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += ']';
|
||||
break;
|
||||
case QJsonValue::Object:
|
||||
json += compact ? "{" : "{\n";
|
||||
objectContentToJson(jsonValue.toObject(), json, indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += '}';
|
||||
break;
|
||||
case QJsonValue::Null:
|
||||
default:
|
||||
json += "null";
|
||||
}
|
||||
}
|
||||
|
||||
QJsonValue QJsonModel::genJson(QJsonTreeItem * item) const
|
||||
|
237
qjsonmodel.h
237
qjsonmodel.h
@ -32,6 +32,235 @@
|
||||
#include <QJsonObject>
|
||||
#include <QIcon>
|
||||
|
||||
namespace QUtf8Functions
|
||||
{
|
||||
/// returns 0 on success; errors can only happen if \a u is a surrogate:
|
||||
/// Error if \a u is a low surrogate;
|
||||
/// if \a u is a high surrogate, Error if the next isn't a low one,
|
||||
/// EndOfString if we run into the end of the string.
|
||||
template <typename Traits, typename OutputPtr, typename InputPtr> inline
|
||||
int toUtf8(ushort u, OutputPtr &dst, InputPtr &src, InputPtr end)
|
||||
{
|
||||
if (!Traits::skipAsciiHandling && u < 0x80)
|
||||
{
|
||||
// U+0000 to U+007F (US-ASCII) - one byte
|
||||
Traits::appendByte(dst, uchar(u));
|
||||
return 0;
|
||||
}
|
||||
else if (u < 0x0800)
|
||||
{
|
||||
// U+0080 to U+07FF - two bytes
|
||||
// first of two bytes
|
||||
Traits::appendByte(dst, 0xc0 | uchar(u >> 6));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!QChar::isSurrogate(u))
|
||||
{
|
||||
// U+0800 to U+FFFF (except U+D800-U+DFFF) - three bytes
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(u))
|
||||
return Traits::Error;
|
||||
// first of three bytes
|
||||
Traits::appendByte(dst, 0xe0 | uchar(u >> 12));
|
||||
}
|
||||
else
|
||||
{
|
||||
// U+10000 to U+10FFFF - four bytes
|
||||
// need to get one extra codepoint
|
||||
if (Traits::availableUtf16(src, end) == 0)
|
||||
return Traits::EndOfString;
|
||||
ushort low = Traits::peekUtf16(src);
|
||||
if (!QChar::isHighSurrogate(u))
|
||||
return Traits::Error;
|
||||
if (!QChar::isLowSurrogate(low))
|
||||
return Traits::Error;
|
||||
Traits::advanceUtf16(src);
|
||||
uint ucs4 = QChar::surrogateToUcs4(u, low);
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(ucs4))
|
||||
return Traits::Error;
|
||||
// first byte
|
||||
Traits::appendByte(dst, 0xf0 | (uchar(ucs4 >> 18) & 0xf));
|
||||
// second of four bytes
|
||||
Traits::appendByte(dst, 0x80 | (uchar(ucs4 >> 12) & 0x3f));
|
||||
// for the rest of the bytes
|
||||
u = ushort(ucs4);
|
||||
}
|
||||
// second to last byte
|
||||
Traits::appendByte(dst, 0x80 | (uchar(u >> 6) & 0x3f));
|
||||
}
|
||||
// last byte
|
||||
Traits::appendByte(dst, 0x80 | (u & 0x3f));
|
||||
return 0;
|
||||
}
|
||||
inline bool isContinuationByte(uchar b)
|
||||
{
|
||||
return (b & 0xc0) == 0x80;
|
||||
}
|
||||
/// returns the number of characters consumed (including \a b) in case of success;
|
||||
/// returns negative in case of error: Traits::Error or Traits::EndOfString
|
||||
template <typename Traits, typename OutputPtr, typename InputPtr> inline
|
||||
int fromUtf8(uchar b, OutputPtr &dst, InputPtr &src, InputPtr end)
|
||||
{
|
||||
int charsNeeded;
|
||||
uint min_uc;
|
||||
uint uc;
|
||||
if (!Traits::skipAsciiHandling && b < 0x80)
|
||||
{
|
||||
// US-ASCII
|
||||
Traits::appendUtf16(dst, b);
|
||||
return 1;
|
||||
}
|
||||
if (!Traits::isTrusted && Q_UNLIKELY(b <= 0xC1))
|
||||
{
|
||||
// an UTF-8 first character must be at least 0xC0
|
||||
// however, all 0xC0 and 0xC1 first bytes can only produce overlong sequences
|
||||
return Traits::Error;
|
||||
}
|
||||
else if (b < 0xe0)
|
||||
{
|
||||
charsNeeded = 2;
|
||||
min_uc = 0x80;
|
||||
uc = b & 0x1f;
|
||||
}
|
||||
else if (b < 0xf0)
|
||||
{
|
||||
charsNeeded = 3;
|
||||
min_uc = 0x800;
|
||||
uc = b & 0x0f;
|
||||
}
|
||||
else if (b < 0xf5)
|
||||
{
|
||||
charsNeeded = 4;
|
||||
min_uc = 0x10000;
|
||||
uc = b & 0x07;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the last Unicode character is U+10FFFF
|
||||
// it's encoded in UTF-8 as "\xF4\x8F\xBF\xBF"
|
||||
// therefore, a byte higher than 0xF4 is not the UTF-8 first byte
|
||||
return Traits::Error;
|
||||
}
|
||||
int bytesAvailable = Traits::availableBytes(src, end);
|
||||
if (Q_UNLIKELY(bytesAvailable < charsNeeded - 1))
|
||||
{
|
||||
// it's possible that we have an error instead of just unfinished bytes
|
||||
if (bytesAvailable > 0 && !isContinuationByte(Traits::peekByte(src, 0)))
|
||||
return Traits::Error;
|
||||
if (bytesAvailable > 1 && !isContinuationByte(Traits::peekByte(src, 1)))
|
||||
return Traits::Error;
|
||||
return Traits::EndOfString;
|
||||
}
|
||||
// first continuation character
|
||||
b = Traits::peekByte(src, 0);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
if (charsNeeded > 2)
|
||||
{
|
||||
// second continuation character
|
||||
b = Traits::peekByte(src, 1);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
if (charsNeeded > 3)
|
||||
{
|
||||
// third continuation character
|
||||
b = Traits::peekByte(src, 2);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
}
|
||||
}
|
||||
// we've decoded something; safety-check it
|
||||
if (!Traits::isTrusted)
|
||||
{
|
||||
if (uc < min_uc)
|
||||
return Traits::Error;
|
||||
if (QChar::isSurrogate(uc) || uc > QChar::LastValidCodePoint)
|
||||
return Traits::Error;
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(uc))
|
||||
return Traits::Error;
|
||||
}
|
||||
// write the UTF-16 sequence
|
||||
if (!QChar::requiresSurrogates(uc))
|
||||
{
|
||||
// UTF-8 decoded and no surrogates are required
|
||||
// detach if necessary
|
||||
Traits::appendUtf16(dst, ushort(uc));
|
||||
}
|
||||
else
|
||||
{
|
||||
// UTF-8 decoded to something that requires a surrogate pair
|
||||
Traits::appendUcs4(dst, uc);
|
||||
}
|
||||
Traits::advanceByte(src, charsNeeded - 1);
|
||||
return charsNeeded;
|
||||
}
|
||||
}
|
||||
|
||||
struct QUtf8BaseTraits
|
||||
{
|
||||
static const bool isTrusted = false;
|
||||
static const bool allowNonCharacters = true;
|
||||
static const bool skipAsciiHandling = false;
|
||||
static const int Error = -1;
|
||||
static const int EndOfString = -2;
|
||||
static bool isValidCharacter(uint u)
|
||||
{
|
||||
return int(u) >= 0;
|
||||
}
|
||||
static void appendByte(uchar *&ptr, uchar b)
|
||||
{
|
||||
*ptr++ = b;
|
||||
}
|
||||
static uchar peekByte(const uchar *ptr, int n = 0)
|
||||
{
|
||||
return ptr[n];
|
||||
}
|
||||
static qptrdiff availableBytes(const uchar *ptr, const uchar *end)
|
||||
{
|
||||
return end - ptr;
|
||||
}
|
||||
static void advanceByte(const uchar *&ptr, int n = 1)
|
||||
{
|
||||
ptr += n;
|
||||
}
|
||||
static void appendUtf16(ushort *&ptr, ushort uc)
|
||||
{
|
||||
*ptr++ = uc;
|
||||
}
|
||||
static void appendUcs4(ushort *&ptr, uint uc)
|
||||
{
|
||||
appendUtf16(ptr, QChar::highSurrogate(uc));
|
||||
appendUtf16(ptr, QChar::lowSurrogate(uc));
|
||||
}
|
||||
static ushort peekUtf16(const ushort *ptr, int n = 0)
|
||||
{
|
||||
return ptr[n];
|
||||
}
|
||||
static qptrdiff availableUtf16(const ushort *ptr, const ushort *end)
|
||||
{
|
||||
return end - ptr;
|
||||
}
|
||||
static void advanceUtf16(const ushort *&ptr, int n = 1)
|
||||
{
|
||||
ptr += n;
|
||||
}
|
||||
// it's possible to output to UCS-4 too
|
||||
static void appendUtf16(uint *&ptr, ushort uc)
|
||||
{
|
||||
*ptr++ = uc;
|
||||
}
|
||||
static void appendUcs4(uint *&ptr, uint uc)
|
||||
{
|
||||
*ptr++ = uc;
|
||||
}
|
||||
};
|
||||
|
||||
class QJsonModel;
|
||||
class QJsonItem;
|
||||
|
||||
@ -86,7 +315,13 @@ public:
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
QJsonDocument json() const;
|
||||
QByteArray json() ;
|
||||
QByteArray jsonToByte(QJsonValue jsonValue);
|
||||
void objectToJson(QJsonObject jsonObject, QByteArray &json, int indent, bool compact);
|
||||
void arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent, bool compact);
|
||||
void arrayContentToJson(QJsonArray jsonArray, QByteArray &json, int indent, bool compact);
|
||||
void objectContentToJson(QJsonObject jsonObject, QByteArray &json, int indent, bool compact);
|
||||
void valueToJson(QJsonValue jsonValue, QByteArray &json, int indent, bool compact);
|
||||
|
||||
private:
|
||||
QJsonValue genJson(QJsonTreeItem *) const;
|
||||
|
Loading…
Reference in New Issue
Block a user