mirror of
https://github.com/open-source-parsers/jsoncpp.git
synced 2025-10-16 07:23:43 +02:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6aba23f4a8 | ||
![]() |
c161f4ac69 | ||
![]() |
75b360af4a | ||
![]() |
e36cff19f0 | ||
![]() |
b8cb8889aa | ||
![]() |
d2d4c74a03 | ||
![]() |
8b7ea09b80 |
1
AUTHORS
1
AUTHORS
@@ -21,6 +21,7 @@ Braden McDorman <bmcdorman@gmail.com>
|
|||||||
Brandon Myers <bmyers1788@gmail.com>
|
Brandon Myers <bmyers1788@gmail.com>
|
||||||
Brendan Drew <brendan.drew@daqri.com>
|
Brendan Drew <brendan.drew@daqri.com>
|
||||||
chason <cxchao802@gmail.com>
|
chason <cxchao802@gmail.com>
|
||||||
|
chenguoping <chenguopingdota@163.com>
|
||||||
Chris Gilling <cgilling@iparadigms.com>
|
Chris Gilling <cgilling@iparadigms.com>
|
||||||
Christopher Dawes <christopher.dawes.1981@googlemail.com>
|
Christopher Dawes <christopher.dawes.1981@googlemail.com>
|
||||||
Christopher Dunn <cdunn2001@gmail.com>
|
Christopher Dunn <cdunn2001@gmail.com>
|
||||||
|
@@ -71,7 +71,7 @@ project(JSONCPP
|
|||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
|
|
||||||
message(STATUS "JsonCpp Version: ${JSONCPP_VERSION_MAJOR}.${JSONCPP_VERSION_MINOR}.${JSONCPP_VERSION_PATCH}")
|
message(STATUS "JsonCpp Version: ${JSONCPP_VERSION_MAJOR}.${JSONCPP_VERSION_MINOR}.${JSONCPP_VERSION_PATCH}")
|
||||||
set(JSONCPP_SOVERSION 23)
|
set(JSONCPP_SOVERSION 24)
|
||||||
|
|
||||||
option(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" ON)
|
option(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" ON)
|
||||||
option(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" ON)
|
option(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" ON)
|
||||||
|
@@ -30,8 +30,14 @@ format to store user input files.
|
|||||||
|
|
||||||
* `1.y.z` is built with C++11.
|
* `1.y.z` is built with C++11.
|
||||||
* `0.y.z` can be used with older compilers.
|
* `0.y.z` can be used with older compilers.
|
||||||
|
* `00.11.z` can be used both in old and new compilers.
|
||||||
* Major versions maintain binary-compatibility.
|
* Major versions maintain binary-compatibility.
|
||||||
|
|
||||||
|
### Special note
|
||||||
|
The branch `00.11.z`is a new branch, its major version number `00` is to show that it is
|
||||||
|
different from `0.y.z` and `1.y.z`, the main purpose of this branch is to make a balance
|
||||||
|
between the other two branches. Thus, users can use some new features in this new branch
|
||||||
|
that introduced in 1.y.z, but can hardly applied into 0.y.z.
|
||||||
|
|
||||||
## Using JsonCpp in your project
|
## Using JsonCpp in your project
|
||||||
|
|
||||||
@@ -42,7 +48,7 @@ You can download and install JsonCpp using the [vcpkg](https://github.com/Micros
|
|||||||
cd vcpkg
|
cd vcpkg
|
||||||
./bootstrap-vcpkg.sh
|
./bootstrap-vcpkg.sh
|
||||||
./vcpkg integrate install
|
./vcpkg integrate install
|
||||||
vcpkg install jsoncpp
|
./vcpkg install jsoncpp
|
||||||
|
|
||||||
The JsonCpp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
The JsonCpp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||||
|
|
||||||
|
@@ -50,7 +50,7 @@ jsoncpp_lib = library(
|
|||||||
'src/lib_json/json_value.cpp',
|
'src/lib_json/json_value.cpp',
|
||||||
'src/lib_json/json_writer.cpp',
|
'src/lib_json/json_writer.cpp',
|
||||||
]),
|
]),
|
||||||
soversion : 23,
|
soversion : 24,
|
||||||
install : true,
|
install : true,
|
||||||
include_directories : jsoncpp_include_directories,
|
include_directories : jsoncpp_include_directories,
|
||||||
cpp_args: dll_export_flag)
|
cpp_args: dll_export_flag)
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#include <json/reader.h>
|
#include <json/reader.h>
|
||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -77,10 +78,7 @@ Features Features::strictMode() {
|
|||||||
// ////////////////////////////////
|
// ////////////////////////////////
|
||||||
|
|
||||||
bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
|
bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
|
||||||
for (; begin < end; ++begin)
|
return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
|
||||||
if (*begin == '\n' || *begin == '\r')
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Class Reader
|
// Class Reader
|
||||||
@@ -998,10 +996,7 @@ private:
|
|||||||
|
|
||||||
bool OurReader::containsNewLine(OurReader::Location begin,
|
bool OurReader::containsNewLine(OurReader::Location begin,
|
||||||
OurReader::Location end) {
|
OurReader::Location end) {
|
||||||
for (; begin < end; ++begin)
|
return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
|
||||||
if (*begin == '\n' || *begin == '\r')
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
|
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
|
||||||
@@ -1275,7 +1270,7 @@ void OurReader::skipSpaces() {
|
|||||||
void OurReader::skipBom(bool skipBom) {
|
void OurReader::skipBom(bool skipBom) {
|
||||||
// The default behavior is to skip BOM.
|
// The default behavior is to skip BOM.
|
||||||
if (skipBom) {
|
if (skipBom) {
|
||||||
if (strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
|
if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
|
||||||
begin_ += 3;
|
begin_ += 3;
|
||||||
current_ = begin_;
|
current_ = begin_;
|
||||||
}
|
}
|
||||||
@@ -1902,38 +1897,34 @@ CharReader* CharReaderBuilder::newCharReader() const {
|
|||||||
features.skipBom_ = settings_["skipBom"].asBool();
|
features.skipBom_ = settings_["skipBom"].asBool();
|
||||||
return new OurCharReader(collectComments, features);
|
return new OurCharReader(collectComments, features);
|
||||||
}
|
}
|
||||||
static void getValidReaderKeys(std::set<String>* valid_keys) {
|
|
||||||
valid_keys->clear();
|
|
||||||
valid_keys->insert("collectComments");
|
|
||||||
valid_keys->insert("allowComments");
|
|
||||||
valid_keys->insert("allowTrailingCommas");
|
|
||||||
valid_keys->insert("strictRoot");
|
|
||||||
valid_keys->insert("allowDroppedNullPlaceholders");
|
|
||||||
valid_keys->insert("allowNumericKeys");
|
|
||||||
valid_keys->insert("allowSingleQuotes");
|
|
||||||
valid_keys->insert("stackLimit");
|
|
||||||
valid_keys->insert("failIfExtra");
|
|
||||||
valid_keys->insert("rejectDupKeys");
|
|
||||||
valid_keys->insert("allowSpecialFloats");
|
|
||||||
valid_keys->insert("skipBom");
|
|
||||||
}
|
|
||||||
bool CharReaderBuilder::validate(Json::Value* invalid) const {
|
bool CharReaderBuilder::validate(Json::Value* invalid) const {
|
||||||
Json::Value my_invalid;
|
static const auto& valid_keys = *new std::set<String>{
|
||||||
if (!invalid)
|
"collectComments",
|
||||||
invalid = &my_invalid; // so we do not need to test for NULL
|
"allowComments",
|
||||||
Json::Value& inv = *invalid;
|
"allowTrailingCommas",
|
||||||
std::set<String> valid_keys;
|
"strictRoot",
|
||||||
getValidReaderKeys(&valid_keys);
|
"allowDroppedNullPlaceholders",
|
||||||
Value::Members keys = settings_.getMemberNames();
|
"allowNumericKeys",
|
||||||
size_t n = keys.size();
|
"allowSingleQuotes",
|
||||||
for (size_t i = 0; i < n; ++i) {
|
"stackLimit",
|
||||||
String const& key = keys[i];
|
"failIfExtra",
|
||||||
if (valid_keys.find(key) == valid_keys.end()) {
|
"rejectDupKeys",
|
||||||
inv[key] = settings_[key];
|
"allowSpecialFloats",
|
||||||
}
|
"skipBom",
|
||||||
|
};
|
||||||
|
for (auto si = settings_.begin(); si != settings_.end(); ++si) {
|
||||||
|
auto key = si.name();
|
||||||
|
if (valid_keys.count(key))
|
||||||
|
continue;
|
||||||
|
if (invalid)
|
||||||
|
(*invalid)[std::move(key)] = *si;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return inv.empty();
|
return invalid ? invalid->empty() : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value& CharReaderBuilder::operator[](const String& key) {
|
Value& CharReaderBuilder::operator[](const String& key) {
|
||||||
return settings_[key];
|
return settings_[key];
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,9 @@
|
|||||||
#include "json_tool.h"
|
#include "json_tool.h"
|
||||||
#include <json/writer.h>
|
#include <json/writer.h>
|
||||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -176,14 +178,9 @@ String valueToString(bool value) { return value ? "true" : "false"; }
|
|||||||
static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
|
static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
|
||||||
assert(s || !n);
|
assert(s || !n);
|
||||||
|
|
||||||
char const* const end = s + n;
|
return std::any_of(s, s + n, [](unsigned char c) {
|
||||||
for (char const* cur = s; cur < end; ++cur) {
|
return c == '\\' || c == '"' || !std::isprint(c);
|
||||||
if (*cur == '\\' || *cur == '\"' ||
|
});
|
||||||
static_cast<unsigned char>(*cur) < ' ' ||
|
|
||||||
static_cast<unsigned char>(*cur) >= 0x80)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
|
static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
|
||||||
@@ -265,6 +262,14 @@ static String toHex16Bit(unsigned int x) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void appendRaw(String& result, unsigned ch) {
|
||||||
|
result += static_cast<char>(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void appendHex(String& result, unsigned ch) {
|
||||||
|
result.append("\\u").append(toHex16Bit(ch));
|
||||||
|
}
|
||||||
|
|
||||||
static String valueToQuotedStringN(const char* value, unsigned length,
|
static String valueToQuotedStringN(const char* value, unsigned length,
|
||||||
bool emitUTF8 = false) {
|
bool emitUTF8 = false) {
|
||||||
if (value == nullptr)
|
if (value == nullptr)
|
||||||
@@ -313,29 +318,26 @@ static String valueToQuotedStringN(const char* value, unsigned length,
|
|||||||
// sequence from occurring.
|
// sequence from occurring.
|
||||||
default: {
|
default: {
|
||||||
if (emitUTF8) {
|
if (emitUTF8) {
|
||||||
result += *c;
|
unsigned codepoint = static_cast<unsigned char>(*c);
|
||||||
|
if (codepoint < 0x20) {
|
||||||
|
appendHex(result, codepoint);
|
||||||
|
} else {
|
||||||
|
appendRaw(result, codepoint);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
unsigned int codepoint = utf8ToCodepoint(c, end);
|
unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
|
||||||
const unsigned int FIRST_NON_CONTROL_CODEPOINT = 0x20;
|
if (codepoint < 0x20) {
|
||||||
const unsigned int LAST_NON_CONTROL_CODEPOINT = 0x7F;
|
appendHex(result, codepoint);
|
||||||
const unsigned int FIRST_SURROGATE_PAIR_CODEPOINT = 0x10000;
|
} else if (codepoint < 0x80) {
|
||||||
// don't escape non-control characters
|
appendRaw(result, codepoint);
|
||||||
// (short escape sequence are applied above)
|
} else if (codepoint < 0x10000) {
|
||||||
if (FIRST_NON_CONTROL_CODEPOINT <= codepoint &&
|
// Basic Multilingual Plane
|
||||||
codepoint <= LAST_NON_CONTROL_CODEPOINT) {
|
appendHex(result, codepoint);
|
||||||
result += static_cast<char>(codepoint);
|
} else {
|
||||||
} else if (codepoint <
|
// Extended Unicode. Encode 20 bits as a surrogate pair.
|
||||||
FIRST_SURROGATE_PAIR_CODEPOINT) { // codepoint is in Basic
|
codepoint -= 0x10000;
|
||||||
// Multilingual Plane
|
appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));
|
||||||
result += "\\u";
|
appendHex(result, 0xdc00 + (codepoint & 0x3ff));
|
||||||
result += toHex16Bit(codepoint);
|
|
||||||
} else { // codepoint is not in Basic Multilingual Plane
|
|
||||||
// convert to surrogate pair first
|
|
||||||
codepoint -= FIRST_SURROGATE_PAIR_CODEPOINT;
|
|
||||||
result += "\\u";
|
|
||||||
result += toHex16Bit((codepoint >> 10) + 0xD800);
|
|
||||||
result += "\\u";
|
|
||||||
result += toHex16Bit((codepoint & 0x3FF) + 0xDC00);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -1198,34 +1200,30 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const {
|
|||||||
endingLineFeedSymbol, usf, emitUTF8, pre,
|
endingLineFeedSymbol, usf, emitUTF8, pre,
|
||||||
precisionType);
|
precisionType);
|
||||||
}
|
}
|
||||||
static void getValidWriterKeys(std::set<String>* valid_keys) {
|
|
||||||
valid_keys->clear();
|
|
||||||
valid_keys->insert("indentation");
|
|
||||||
valid_keys->insert("commentStyle");
|
|
||||||
valid_keys->insert("enableYAMLCompatibility");
|
|
||||||
valid_keys->insert("dropNullPlaceholders");
|
|
||||||
valid_keys->insert("useSpecialFloats");
|
|
||||||
valid_keys->insert("emitUTF8");
|
|
||||||
valid_keys->insert("precision");
|
|
||||||
valid_keys->insert("precisionType");
|
|
||||||
}
|
|
||||||
bool StreamWriterBuilder::validate(Json::Value* invalid) const {
|
bool StreamWriterBuilder::validate(Json::Value* invalid) const {
|
||||||
Json::Value my_invalid;
|
static const auto& valid_keys = *new std::set<String>{
|
||||||
if (!invalid)
|
"indentation",
|
||||||
invalid = &my_invalid; // so we do not need to test for NULL
|
"commentStyle",
|
||||||
Json::Value& inv = *invalid;
|
"enableYAMLCompatibility",
|
||||||
std::set<String> valid_keys;
|
"dropNullPlaceholders",
|
||||||
getValidWriterKeys(&valid_keys);
|
"useSpecialFloats",
|
||||||
Value::Members keys = settings_.getMemberNames();
|
"emitUTF8",
|
||||||
size_t n = keys.size();
|
"precision",
|
||||||
for (size_t i = 0; i < n; ++i) {
|
"precisionType",
|
||||||
String const& key = keys[i];
|
};
|
||||||
if (valid_keys.find(key) == valid_keys.end()) {
|
for (auto si = settings_.begin(); si != settings_.end(); ++si) {
|
||||||
inv[key] = settings_[key];
|
auto key = si.name();
|
||||||
}
|
if (valid_keys.count(key))
|
||||||
|
continue;
|
||||||
|
if (invalid)
|
||||||
|
(*invalid)[std::move(key)] = *si;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return inv.empty();
|
return invalid ? invalid->empty() : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value& StreamWriterBuilder::operator[](const String& key) {
|
Value& StreamWriterBuilder::operator[](const String& key) {
|
||||||
return settings_[key];
|
return settings_[key];
|
||||||
}
|
}
|
||||||
|
@@ -2640,6 +2640,68 @@ JSONTEST_FIXTURE_LOCAL(StreamWriterTest, unicode) {
|
|||||||
"\"\\t\\n\\ud806\\udca1=\\u0133\\ud82c\\udd1b\\uff67\"\n}");
|
"\"\\t\\n\\ud806\\udca1=\\u0133\\ud82c\\udd1b\\uff67\"\n}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Control chars should be escaped regardless of UTF-8 input encoding.
|
||||||
|
JSONTEST_FIXTURE_LOCAL(StreamWriterTest, escapeControlCharacters) {
|
||||||
|
auto uEscape = [](unsigned ch) {
|
||||||
|
static const char h[] = "0123456789abcdef";
|
||||||
|
std::string r = "\\u";
|
||||||
|
r += h[(ch >> (3 * 4)) & 0xf];
|
||||||
|
r += h[(ch >> (2 * 4)) & 0xf];
|
||||||
|
r += h[(ch >> (1 * 4)) & 0xf];
|
||||||
|
r += h[(ch >> (0 * 4)) & 0xf];
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
auto shortEscape = [](unsigned ch) -> const char* {
|
||||||
|
switch (ch) {
|
||||||
|
case '\"':
|
||||||
|
return "\\\"";
|
||||||
|
case '\\':
|
||||||
|
return "\\\\";
|
||||||
|
case '\b':
|
||||||
|
return "\\b";
|
||||||
|
case '\f':
|
||||||
|
return "\\f";
|
||||||
|
case '\n':
|
||||||
|
return "\\n";
|
||||||
|
case '\r':
|
||||||
|
return "\\r";
|
||||||
|
case '\t':
|
||||||
|
return "\\t";
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Json::StreamWriterBuilder b;
|
||||||
|
|
||||||
|
for (bool emitUTF8 : {true, false}) {
|
||||||
|
b.settings_["emitUTF8"] = emitUTF8;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i != 0x100; ++i) {
|
||||||
|
if (!emitUTF8 && i >= 0x80)
|
||||||
|
break; // The algorithm would try to parse UTF-8, so stop here.
|
||||||
|
|
||||||
|
std::string raw({static_cast<char>(i)});
|
||||||
|
std::string esc = raw;
|
||||||
|
if (i < 0x20)
|
||||||
|
esc = uEscape(i);
|
||||||
|
if (const char* shEsc = shortEscape(i))
|
||||||
|
esc = shEsc;
|
||||||
|
|
||||||
|
// std::cout << "emit=" << emitUTF8 << ", i=" << std::hex << i << std::dec
|
||||||
|
// << std::endl;
|
||||||
|
|
||||||
|
Json::Value root;
|
||||||
|
root["test"] = raw;
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(
|
||||||
|
std::string("{\n\t\"test\" : \"").append(esc).append("\"\n}"),
|
||||||
|
Json::writeString(b, root))
|
||||||
|
<< ", emit=" << emitUTF8 << ", i=" << i << ", raw=\"" << raw << "\""
|
||||||
|
<< ", esc=\"" << esc << "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ReaderTest : JsonTest::TestCase {
|
struct ReaderTest : JsonTest::TestCase {
|
||||||
void setStrictMode() {
|
void setStrictMode() {
|
||||||
reader = std::unique_ptr<Json::Reader>(
|
reader = std::unique_ptr<Json::Reader>(
|
||||||
@@ -3286,11 +3348,11 @@ struct CharReaderAllowDropNullTest : JsonTest::TestCase {
|
|||||||
return [=](const Value& root) { JSONTEST_ASSERT_EQUAL(root, v); };
|
return [=](const Value& root) { JSONTEST_ASSERT_EQUAL(root, v); };
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueCheck objGetAnd(std::string idx, ValueCheck f) {
|
static ValueCheck objGetAnd(std::string idx, ValueCheck f) {
|
||||||
return [=](const Value& root) { f(root.get(idx, true)); };
|
return [=](const Value& root) { f(root.get(idx, true)); };
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueCheck arrGetAnd(int idx, ValueCheck f) {
|
static ValueCheck arrGetAnd(int idx, ValueCheck f) {
|
||||||
return [=](const Value& root) { f(root[idx]); };
|
return [=](const Value& root) { f(root[idx]); };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user