Compare commits

..

22 Commits
0.8.1 ... 1.4.4

Author SHA1 Message Date
Christopher Dunn
f9db82af17 1.4.4 < 1.4.3
* fixed Readers when `allowDropNullPlaceholders`
2015-02-19 15:37:57 -06:00
Christopher Dunn
ae71879549 Merge pull request #179 from cdunn2001/drop-null
Fix failures for `allowDroppedNullPlaceholders`.
2015-02-19 12:38:16 -06:00
Christopher Dunn
7b3683ccd1 apply fix to old Reader 2015-02-19 11:37:17 -06:00
Christopher Dunn
58499031a4 fix all cases from issue -- all pass! 2015-02-19 11:35:28 -06:00
Christopher Dunn
2c8c1ac0ec all 8 failing cases from issue 2015-02-19 11:35:20 -06:00
Christopher Dunn
c58e93b014 fix failing object case 2015-02-19 11:34:35 -06:00
Christopher Dunn
eed193e151 object cases; 1st passes, 2nd fails 2015-02-19 11:34:35 -06:00
Christopher Dunn
4382a7b585 cases 0,1,5,9,12 from issue -- passing 2015-02-19 11:32:42 -06:00
Christopher Dunn
30d923f155 1.4.3 <- 1.4.2 2015-02-18 09:20:28 -06:00
Christopher Dunn
2f4e40bc95 fix typo: issue #177 2015-02-18 09:17:06 -06:00
Christopher Dunn
505e086ebc Merge pull request #173 from cdunn2001/fix-include-amal
help use of amalgamated header

Reviewed by "Lightness Races in Orbit" at StackOverflow.
2015-02-15 13:11:38 -06:00
Christopher Dunn
c6582415d8 fix typos 2015-02-15 13:06:48 -06:00
Christopher Dunn
0ee7e2426f help use of amalgamated header
* Include from same-directory first, so `-I DIR` is rarely needed.
* Double-check that proper header has been included.

http://stackoverflow.com/questions/28510109/linking-problems-with-jsoncpp
2015-02-15 13:06:48 -06:00
Christopher Dunn
1522e4dfb1 Merge pull request #174 from kobigurk/kobigurk-patch-asserts
only throws exceptions JSON_USE_EXCEPTION
2015-02-15 12:05:28 -06:00
Kobi Gurkan
09b8670536 only throws exceptions JSON_USE_EXCEPTION
JSON_ASSERT now throws a runtime_error
2015-02-15 18:00:31 +02:00
Christopher Dunn
f164288646 help rebasing 2015-02-15 03:01:26 -06:00
Christopher Dunn
3bfd215938 1.4.2 < 1.4.1
* Bug-fix for ValueIterator::operator-() (issue #169)
2015-02-15 02:49:34 -06:00
Christopher Dunn
400b744195 Merge pull request #172 from cdunn2001/master
Fix bug in ValueIteratorBase::operator- 

Fixes #169.
2015-02-15 02:44:17 -06:00
Christopher Dunn
bd55164089 reverse sense for CPPTL too 2015-02-15 02:38:31 -06:00
Kevin Grant
4c5832a0be Fix bug in ValueIteratorBase::operator- 2015-02-15 02:38:31 -06:00
Christopher Dunn
8ba9875962 IteratorTest 2015-02-15 02:38:31 -06:00
Christopher Dunn
9c91b995dd rules for signing and doc-generation 2015-02-14 10:20:45 -06:00
15 changed files with 583 additions and 60 deletions

View File

@@ -85,10 +85,10 @@ endif( MSVC )
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# using regular Clang or AppleClang
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# using GCC
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall -Wextra -pedantic")
endif()
IF(JSONCPP_WITH_WARNING_AS_ERROR)

View File

@@ -59,7 +59,7 @@ def amalgamate_source(source_top_dir=None,
print("Amalgating header...")
header = AmalgamationFile(source_top_dir)
header.add_text("/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/).")
header.add_text("/// It is intented to be used with #include <%s>" % header_include_path)
header.add_text('/// It is intended to be used with #include "%s"' % header_include_path)
header.add_file("LICENSE", wrap_in_comment=True)
header.add_text("#ifndef JSON_AMALGATED_H_INCLUDED")
header.add_text("# define JSON_AMALGATED_H_INCLUDED")
@@ -85,7 +85,7 @@ def amalgamate_source(source_top_dir=None,
print("Amalgating forward header...")
header = AmalgamationFile(source_top_dir)
header.add_text("/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).")
header.add_text("/// It is intented to be used with #include <%s>" % forward_header_include_path)
header.add_text('/// It is intended to be used with #include "%s"' % forward_header_include_path)
header.add_text("/// This header provides forward declaration for all JsonCpp types.")
header.add_file("LICENSE", wrap_in_comment=True)
header.add_text("#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED")
@@ -105,10 +105,15 @@ def amalgamate_source(source_top_dir=None,
print("Amalgating source...")
source = AmalgamationFile(source_top_dir)
source.add_text("/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/).")
source.add_text("/// It is intented to be used with #include <%s>" % header_include_path)
source.add_text('/// It is intended to be used with #include "%s"' % header_include_path)
source.add_file("LICENSE", wrap_in_comment=True)
source.add_text("")
source.add_text("#include <%s>" % header_include_path)
source.add_text('#include "%s"' % header_include_path)
source.add_text("""
#ifndef JSON_IS_AMALGAMATION
#error "Compile with -I PATH_TO_JSON_DIRECTORY"
#endif
""")
source.add_text("")
lib_json = "src/lib_json"
source.add_file(os.path.join(lib_json, "json_tool.h"))

View File

@@ -1,5 +1,19 @@
all: build test-amalgamate
# This is only for jsoncpp developers/contributors.
# We use this to sign releases, generate documentation, etc.
VER?=$(shell cat version)
default:
@echo "VER=${VER}"
sign: jsoncpp-${VER}.tar.gz
gpg --armor --detach-sign $<
gpg --verify $<.asc
# Then upload .asc to the release.
jsoncpp-%.tar.gz:
curl https://github.com/open-source-parsers/jsoncpp/archive/$*.tar.gz -o $@
dox:
python doxybuild.py --doxygen=$$(which doxygen) --in doc/web_doxyfile.in
rsync -va --delete dist/doxygen/jsoncpp-api-html-${VER}/ ../jsoncpp-docs/doxygen/
# Then 'git add -A' and 'git push' in jsoncpp-docs.
build:
mkdir -p build/debug
cd build/debug; cmake -DCMAKE_BUILD_TYPE=debug -DJSONCPP_LIB_BUILD_SHARED=ON -G "Unix Makefiles" ../..
@@ -7,8 +21,11 @@ build:
# Currently, this depends on include/json/version.h generated
# by cmake.
test-amalgamate: build
test-amalgamate:
python2.7 amalgamate.py
python3.4 amalgamate.py
clean:
\rm -rf *.gz *.asc dist/
.PHONY: build

View File

@@ -16,7 +16,7 @@
#if JSON_USE_EXCEPTION
#include <stdexcept>
#define JSON_ASSERT(condition) \
assert(condition); // @todo <= change this into an exception throw
if (!(condition)) {throw std::runtime_error( "assert json failed" );} // @todo <= add detail about condition in exception
#define JSON_FAIL_MESSAGE(message) do{std::ostringstream oss; oss << message; throw std::runtime_error(oss.str());}while(0)
//#define JSON_FAIL_MESSAGE(message) throw std::runtime_error(message)
#else // JSON_USE_EXCEPTION

View File

@@ -44,6 +44,12 @@ public:
/// \c true if root must be either an array or an object value. Default: \c
/// false.
bool strictRoot_;
/// \c true if dropped null placeholders are allowed. Default: \c false.
bool allowDroppedNullPlaceholders_;
/// \c true if numeric object key are allowed. Default: \c false.
bool allowNumericKeys_;
};
} // namespace Json

View File

@@ -35,6 +35,18 @@ public:
typedef char Char;
typedef const Char* Location;
/** \brief An error tagged with where in the JSON text it was encountered.
*
* The offsets give the [start, limit) range of bytes within the text. Note
* that this is bytes, not codepoints.
*
*/
struct StructuredError {
size_t offset_start;
size_t offset_limit;
std::string message;
};
/** \brief Constructs a Reader allowing all features
* for parsing.
*/
@@ -111,6 +123,38 @@ public:
*/
std::string getFormattedErrorMessages() const;
/** \brief Returns a vector of structured erros encounted while parsing.
* \return A (possibly empty) vector of StructuredError objects. Currently
* only one error can be returned, but the caller should tolerate
* multiple
* errors. This can occur if the parser recovers from a non-fatal
* parse error and then encounters additional errors.
*/
std::vector<StructuredError> getStructuredErrors() const;
/** \brief Add a semantic error message.
* \param value JSON Value location associated with the error
* \param message The error message.
* \return \c true if the error was successfully added, \c false if the
* Value offset exceeds the document size.
*/
bool pushError(const Value& value, const std::string& message);
/** \brief Add a semantic error message with extra context.
* \param value JSON Value location associated with the error
* \param message The error message.
* \param extra Additional JSON Value location to contextualize the error
* \return \c true if the error was successfully added, \c false if either
* Value offset exceeds the document size.
*/
bool pushError(const Value& value, const std::string& message, const Value& extra);
/** \brief Return whether there are any errors.
* \return \c true if there are no errors to report \c false if
* errors have occurred.
*/
bool good() const;
private:
enum TokenType {
tokenEndOfStream = 0,

View File

@@ -133,11 +133,8 @@ public:
typedef Json::LargestUInt LargestUInt;
typedef Json::ArrayIndex ArrayIndex;
static const Value& nullRef;
#if !defined(__ARMEL__)
/// \deprecated This exists for binary compatibility only. Use nullRef.
static const Value null;
#endif
static const Value& null; ///! We regret this reference to a global instance; prefer the simpler Value().
static const Value& nullRef; ///! just a kludge for binary-compatibility; same as null
/// Minimum signed integer value that can be stored in a Json::Value.
static const LargestInt minLargestInt;
/// Maximum signed integer value that can be stored in a Json::Value.
@@ -175,7 +172,7 @@ private:
CZString(const char* cstr, DuplicationPolicy allocate);
CZString(const CZString& other);
~CZString();
CZString &operator=(const CZString &other);
CZString& operator=(CZString other);
bool operator<(const CZString& other) const;
bool operator==(const CZString& other) const;
ArrayIndex index() const;
@@ -244,7 +241,7 @@ Json::Value obj_value(Json::objectValue); // {}
~Value();
// Deep copy, then swap(other).
Value &operator=(const Value &other);
Value& operator=(Value other);
/// Swap everything.
void swap(Value& other);
/// Swap values but leave comments and source offsets in place.
@@ -454,6 +451,13 @@ Json::Value obj_value(Json::objectValue); // {}
iterator begin();
iterator end();
// Accessors for the [start, limit) range of bytes within the JSON text from
// which this value was parsed, if any.
void setOffsetStart(size_t start);
void setOffsetLimit(size_t limit);
size_t getOffsetStart() const;
size_t getOffsetLimit() const;
private:
void initBasic(ValueType type, bool allocated = false);
@@ -510,6 +514,11 @@ private:
unsigned int memberNameIsStatic_ : 1; // used by the ValueInternalMap container.
#endif
CommentInfo* comments_;
// [start, limit) byte offsets in the source JSON text from which this Value
// was extracted.
size_t start_;
size_t limit_;
};
/** \brief Experimental and untested: represents an element of the "path" to
@@ -937,7 +946,7 @@ public:
bool operator!=(const SelfType& other) const { return !isEqual(other); }
difference_type operator-(const SelfType& other) const {
return computeDistance(other);
return other.computeDistance(*this);
}
/// Return either the index or the member name of the referenced value as a

View File

@@ -4,10 +4,10 @@
#ifndef JSON_VERSION_H_INCLUDED
# define JSON_VERSION_H_INCLUDED
# define JSONCPP_VERSION_STRING "0.8.1"
# define JSONCPP_VERSION_MAJOR 0
# define JSONCPP_VERSION_MINOR 8
# define JSONCPP_VERSION_PATCH 1
# define JSONCPP_VERSION_STRING "1.4.4"
# define JSONCPP_VERSION_MAJOR 1
# define JSONCPP_VERSION_MINOR 4
# define JSONCPP_VERSION_PATCH 4
# define JSONCPP_VERSION_QUALIFIER
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))

View File

@@ -153,6 +153,15 @@ public:
void enableYAMLCompatibility();
/** \brief Drop the "null" string from the writer's output for nullValues.
* Strictly speaking, this is not valid JSON. But when the output is being
* fed to a browser's Javascript, it makes for smaller output and the
* browser can handle the output just fine.
*/
void dropNullPlaceholders();
void omitEndingLineFeed();
public: // overridden from Writer
virtual std::string write(const Value& root);
@@ -161,6 +170,8 @@ private:
std::string document_;
bool yamlCompatiblityEnabled_;
bool dropNullPlaceholders_;
bool omitEndingLineFeed_;
};
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a

View File

@@ -43,14 +43,17 @@ typedef std::auto_ptr<CharReader> CharReaderPtr;
// ////////////////////////////////
Features::Features()
: allowComments_(true), strictRoot_(false)
{}
: allowComments_(true), strictRoot_(false),
allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}
Features Features::all() { return Features(); }
Features Features::strictMode() {
Features features;
features.allowComments_ = false;
features.strictRoot_ = true;
features.allowDroppedNullPlaceholders_ = false;
features.allowNumericKeys_ = false;
return features;
}
@@ -160,9 +163,11 @@ bool Reader::readValue() {
switch (token.type_) {
case tokenObjectBegin:
successful = readObject(token);
currentValue().setOffsetLimit(current_ - begin_);
break;
case tokenArrayBegin:
successful = readArray(token);
currentValue().setOffsetLimit(current_ - begin_);
break;
case tokenNumber:
successful = decodeNumber(token);
@@ -174,22 +179,42 @@ bool Reader::readValue() {
{
Value v(true);
currentValue().swapPayload(v);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
}
break;
case tokenFalse:
{
Value v(false);
currentValue().swapPayload(v);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
}
break;
case tokenNull:
{
Value v;
currentValue().swapPayload(v);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
}
break;
// Else, fall through...
case tokenArraySeparator:
case tokenObjectEnd:
case tokenArrayEnd:
if (features_.allowDroppedNullPlaceholders_) {
// "Un-read" the current token and mark the current value as a null
// token.
current_--;
Value v;
currentValue().swapPayload(v);
currentValue().setOffsetStart(current_ - begin_ - 1);
currentValue().setOffsetLimit(current_ - begin_);
break;
} // Else, fall through...
default:
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return addError("Syntax error: value, object or array expected.", token);
}
@@ -417,11 +442,12 @@ bool Reader::readString() {
return c == '"';
}
bool Reader::readObject(Token& /*tokenStart*/) {
bool Reader::readObject(Token& tokenStart) {
Token tokenName;
std::string name;
Value init(objectValue);
currentValue().swapPayload(init);
currentValue().setOffsetStart(tokenStart.start_ - begin_);
while (readToken(tokenName)) {
bool initialTokenOk = true;
while (tokenName.type_ == tokenComment && initialTokenOk)
@@ -434,6 +460,11 @@ bool Reader::readObject(Token& /*tokenStart*/) {
if (tokenName.type_ == tokenString) {
if (!decodeString(tokenName, name))
return recoverFromError(tokenObjectEnd);
} else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
Value numberName;
if (!decodeNumber(tokenName, numberName))
return recoverFromError(tokenObjectEnd);
name = numberName.asString();
} else {
break;
}
@@ -467,9 +498,10 @@ bool Reader::readObject(Token& /*tokenStart*/) {
"Missing '}' or object member name", tokenName, tokenObjectEnd);
}
bool Reader::readArray(Token& /*tokenStart*/) {
bool Reader::readArray(Token& tokenStart) {
Value init(arrayValue);
currentValue().swapPayload(init);
currentValue().setOffsetStart(tokenStart.start_ - begin_);
skipSpaces();
if (*current_ == ']') // empty array
{
@@ -509,6 +541,8 @@ bool Reader::decodeNumber(Token& token) {
if (!decodeNumber(token, decoded))
return false;
currentValue().swapPayload(decoded);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -557,6 +591,8 @@ bool Reader::decodeDouble(Token& token) {
if (!decodeDouble(token, decoded))
return false;
currentValue().swapPayload(decoded);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -602,6 +638,8 @@ bool Reader::decodeString(Token& token) {
return false;
Value decoded(decoded_string);
currentValue().swapPayload(decoded);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -813,8 +851,59 @@ std::string Reader::getFormattedErrorMessages() const {
return formattedMessage;
}
// Reader
/////////////////////////
std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
std::vector<Reader::StructuredError> allErrors;
for (Errors::const_iterator itError = errors_.begin();
itError != errors_.end();
++itError) {
const ErrorInfo& error = *itError;
Reader::StructuredError structured;
structured.offset_start = error.token_.start_ - begin_;
structured.offset_limit = error.token_.end_ - begin_;
structured.message = error.message_;
allErrors.push_back(structured);
}
return allErrors;
}
bool Reader::pushError(const Value& value, const std::string& message) {
size_t length = end_ - begin_;
if(value.getOffsetStart() > length
|| value.getOffsetLimit() > length)
return false;
Token token;
token.type_ = tokenError;
token.start_ = begin_ + value.getOffsetStart();
token.end_ = end_ + value.getOffsetLimit();
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = 0;
errors_.push_back(info);
return true;
}
bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) {
size_t length = end_ - begin_;
if(value.getOffsetStart() > length
|| value.getOffsetLimit() > length
|| extra.getOffsetLimit() > length)
return false;
Token token;
token.type_ = tokenError;
token.start_ = begin_ + value.getOffsetStart();
token.end_ = begin_ + value.getOffsetLimit();
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = begin_ + extra.getOffsetStart();
errors_.push_back(info);
return true;
}
bool Reader::good() const {
return !errors_.size();
}
// exact copy of Features
class OurFeatures {
@@ -868,6 +957,10 @@ public:
Value& root,
bool collectComments = true);
std::string getFormattedErrorMessages() const;
std::vector<StructuredError> getStructuredErrors() const;
bool pushError(const Value& value, const std::string& message);
bool pushError(const Value& value, const std::string& message, const Value& extra);
bool good() const;
private:
OurReader(OurReader const&); // no impl
@@ -1031,9 +1124,11 @@ bool OurReader::readValue() {
switch (token.type_) {
case tokenObjectBegin:
successful = readObject(token);
currentValue().setOffsetLimit(current_ - begin_);
break;
case tokenArrayBegin:
successful = readArray(token);
currentValue().setOffsetLimit(current_ - begin_);
break;
case tokenNumber:
successful = decodeNumber(token);
@@ -1045,31 +1140,42 @@ bool OurReader::readValue() {
{
Value v(true);
currentValue().swapPayload(v);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
}
break;
case tokenFalse:
{
Value v(false);
currentValue().swapPayload(v);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
}
break;
case tokenNull:
{
Value v;
currentValue().swapPayload(v);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
}
break;
case tokenArraySeparator:
case tokenObjectEnd:
case tokenArrayEnd:
if (features_.allowDroppedNullPlaceholders_) {
// "Un-read" the current token and mark the current value as a null
// token.
current_--;
Value v;
currentValue().swapPayload(v);
currentValue().setOffsetStart(current_ - begin_ - 1);
currentValue().setOffsetLimit(current_ - begin_);
break;
}
// Else, fall through...
} // else, fall through ...
default:
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return addError("Syntax error: value, object or array expected.", token);
}
@@ -1283,6 +1389,7 @@ bool OurReader::readObject(Token& tokenStart) {
std::string name;
Value init(objectValue);
currentValue().swapPayload(init);
currentValue().setOffsetStart(tokenStart.start_ - begin_);
while (readToken(tokenName)) {
bool initialTokenOk = true;
while (tokenName.type_ == tokenComment && initialTokenOk)
@@ -1336,6 +1443,7 @@ bool OurReader::readObject(Token& tokenStart) {
bool OurReader::readArray(Token& tokenStart) {
Value init(arrayValue);
currentValue().swapPayload(init);
currentValue().setOffsetStart(tokenStart.start_ - begin_);
skipSpaces();
if (*current_ == ']') // empty array
{
@@ -1375,6 +1483,8 @@ bool OurReader::decodeNumber(Token& token) {
if (!decodeNumber(token, decoded))
return false;
currentValue().swapPayload(decoded);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -1423,6 +1533,8 @@ bool OurReader::decodeDouble(Token& token) {
if (!decodeDouble(token, decoded))
return false;
currentValue().swapPayload(decoded);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -1468,6 +1580,8 @@ bool OurReader::decodeString(Token& token) {
return false;
Value decoded(decoded_string);
currentValue().swapPayload(decoded);
currentValue().setOffsetStart(token.start_ - begin_);
currentValue().setOffsetLimit(token.end_ - begin_);
return true;
}
@@ -1674,6 +1788,60 @@ std::string OurReader::getFormattedErrorMessages() const {
return formattedMessage;
}
std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
std::vector<OurReader::StructuredError> allErrors;
for (Errors::const_iterator itError = errors_.begin();
itError != errors_.end();
++itError) {
const ErrorInfo& error = *itError;
OurReader::StructuredError structured;
structured.offset_start = error.token_.start_ - begin_;
structured.offset_limit = error.token_.end_ - begin_;
structured.message = error.message_;
allErrors.push_back(structured);
}
return allErrors;
}
bool OurReader::pushError(const Value& value, const std::string& message) {
size_t length = end_ - begin_;
if(value.getOffsetStart() > length
|| value.getOffsetLimit() > length)
return false;
Token token;
token.type_ = tokenError;
token.start_ = begin_ + value.getOffsetStart();
token.end_ = end_ + value.getOffsetLimit();
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = 0;
errors_.push_back(info);
return true;
}
bool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) {
size_t length = end_ - begin_;
if(value.getOffsetStart() > length
|| value.getOffsetLimit() > length
|| extra.getOffsetLimit() > length)
return false;
Token token;
token.type_ = tokenError;
token.start_ = begin_ + value.getOffsetStart();
token.end_ = begin_ + value.getOffsetLimit();
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = begin_ + extra.getOffsetStart();
errors_.push_back(info);
return true;
}
bool OurReader::good() const {
return !errors_.size();
}
class OurCharReader : public CharReader {
bool const collectComments_;

View File

@@ -31,13 +31,12 @@ namespace Json {
#if defined(__ARMEL__)
#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
#else
// This exists for binary compatibility only. Use nullRef.
static const Value null;
#define ALIGNAS(byte_alignment)
#endif
static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
const unsigned char& kNullRef = kNull[0];
const Value& Value::nullRef = reinterpret_cast<const Value&>(kNullRef);
const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
const Value& Value::nullRef = null;
const Int Value::minInt = Int(~(UInt(-1) / 2));
const Int Value::maxInt = Int(UInt(-1) / 2);
@@ -193,9 +192,8 @@ void Value::CZString::swap(CZString& other) {
std::swap(index_, other.index_);
}
Value::CZString &Value::CZString::operator=(const CZString &other) {
CZString temp(other);
swap(temp);
Value::CZString& Value::CZString::operator=(CZString other) {
swap(other);
return *this;
}
@@ -333,7 +331,7 @@ Value::Value(const Value& other)
itemIsUsed_(0)
#endif
,
comments_(0) {
comments_(0), start_(other.start_), limit_(other.limit_) {
switch (type_) {
case nullValue:
case intValue:
@@ -411,9 +409,8 @@ Value::~Value() {
delete[] comments_;
}
Value &Value::operator=(const Value &other) {
Value temp(other);
swap(temp);
Value& Value::operator=(Value other) {
swap(other);
return *this;
}
@@ -430,6 +427,8 @@ void Value::swapPayload(Value& other) {
void Value::swap(Value& other) {
swapPayload(other);
std::swap(comments_, other.comments_);
std::swap(start_, other.start_);
std::swap(limit_, other.limit_);
}
ValueType Value::type() const { return type_; }
@@ -804,6 +803,8 @@ void Value::clear() {
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue ||
type_ == objectValue,
"in Json::Value::clear(): requires complex value");
start_ = 0;
limit_ = 0;
switch (type_) {
#ifndef JSON_VALUE_USE_INTERNAL_MAP
case arrayValue:
@@ -857,7 +858,7 @@ Value& Value::operator[](ArrayIndex index) {
if (it != value_.map_->end() && (*it).first == key)
return (*it).second;
ObjectValues::value_type defaultValue(key, nullRef);
ObjectValues::value_type defaultValue(key, null);
it = value_.map_->insert(it, defaultValue);
return (*it).second;
#else
@@ -877,16 +878,16 @@ const Value& Value::operator[](ArrayIndex index) const {
type_ == nullValue || type_ == arrayValue,
"in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
if (type_ == nullValue)
return nullRef;
return null;
#ifndef JSON_VALUE_USE_INTERNAL_MAP
CZString key(index);
ObjectValues::const_iterator it = value_.map_->find(key);
if (it == value_.map_->end())
return nullRef;
return null;
return (*it).second;
#else
Value* value = value_.array_->find(index);
return value ? *value : nullRef;
return value ? *value : null;
#endif
}
@@ -908,6 +909,8 @@ void Value::initBasic(ValueType type, bool allocated) {
itemIsUsed_ = 0;
#endif
comments_ = 0;
start_ = 0;
limit_ = 0;
}
Value& Value::resolveReference(const char* key, bool isStatic) {
@@ -923,7 +926,7 @@ Value& Value::resolveReference(const char* key, bool isStatic) {
if (it != value_.map_->end() && (*it).first == actualKey)
return (*it).second;
ObjectValues::value_type defaultValue(actualKey, nullRef);
ObjectValues::value_type defaultValue(actualKey, null);
it = value_.map_->insert(it, defaultValue);
Value& value = (*it).second;
return value;
@@ -934,7 +937,7 @@ Value& Value::resolveReference(const char* key, bool isStatic) {
Value Value::get(ArrayIndex index, const Value& defaultValue) const {
const Value* value = &((*this)[index]);
return value == &nullRef ? defaultValue : *value;
return value == &null ? defaultValue : *value;
}
bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
@@ -944,16 +947,16 @@ const Value& Value::operator[](const char* key) const {
type_ == nullValue || type_ == objectValue,
"in Json::Value::operator[](char const*)const: requires objectValue");
if (type_ == nullValue)
return nullRef;
return null;
#ifndef JSON_VALUE_USE_INTERNAL_MAP
CZString actualKey(key, CZString::noDuplication);
ObjectValues::const_iterator it = value_.map_->find(actualKey);
if (it == value_.map_->end())
return nullRef;
return null;
return (*it).second;
#else
const Value* value = value_.map_->find(key);
return value ? *value : nullRef;
return value ? *value : null;
#endif
}
@@ -983,7 +986,7 @@ Value& Value::append(const Value& value) { return (*this)[size()] = value; }
Value Value::get(const char* key, const Value& defaultValue) const {
const Value* value = &((*this)[key]);
return value == &nullRef ? defaultValue : *value;
return value == &null ? defaultValue : *value;
}
Value Value::get(const std::string& key, const Value& defaultValue) const {
@@ -1019,7 +1022,7 @@ Value Value::removeMember(const char* key) {
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
"in Json::Value::removeMember(): requires objectValue");
if (type_ == nullValue)
return nullRef;
return null;
Value removed; // null
removeMember(key, &removed);
@@ -1067,7 +1070,7 @@ Value Value::get(const CppTL::ConstString& key,
bool Value::isMember(const char* key) const {
const Value* value = &((*this)[key]);
return value != &nullRef;
return value != &null;
}
bool Value::isMember(const std::string& key) const {
@@ -1254,6 +1257,14 @@ std::string Value::getComment(CommentPlacement placement) const {
return "";
}
void Value::setOffsetStart(size_t start) { start_ = start; }
void Value::setOffsetLimit(size_t limit) { limit_ = limit; }
size_t Value::getOffsetStart() const { return start_; }
size_t Value::getOffsetLimit() const { return limit_; }
std::string Value::toStyledString() const {
StyledWriter writer;
return writer.write(*this);
@@ -1473,7 +1484,7 @@ const Value& Path::resolve(const Value& root) const {
// Error: unable to resolve path (object value expected at position...)
}
node = &((*node)[arg.key_]);
if (node == &Value::nullRef) {
if (node == &Value::null) {
// Error: unable to resolve path (object has no member named '' at
// position...)
}
@@ -1494,7 +1505,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const {
if (!node->isObject())
return defaultValue;
node = &((*node)[arg.key_]);
if (node == &Value::nullRef)
if (node == &Value::null)
return defaultValue;
}
}

View File

@@ -77,7 +77,7 @@ ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance(const SelfType& other) const {
#ifndef JSON_VALUE_USE_INTERNAL_MAP
#ifdef JSON_USE_CPPTL_SMALLMAP
return current_ - other.current_;
return other.current_ - current_;
#else
// Iterator for null value are initialized using the default
// constructor, which initialize current_ to the default

View File

@@ -192,13 +192,19 @@ Writer::~Writer() {}
// //////////////////////////////////////////////////////////////////
FastWriter::FastWriter()
: yamlCompatiblityEnabled_(false) {}
: yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
omitEndingLineFeed_(false) {}
void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
std::string FastWriter::write(const Value& root) {
document_ = "";
writeValue(root);
if (!omitEndingLineFeed_)
document_ += "\n";
return document_;
}
@@ -206,6 +212,7 @@ std::string FastWriter::write(const Value& root) {
void FastWriter::writeValue(const Value& value) {
switch (value.type()) {
case nullValue:
if (!dropNullPlaceholders_)
document_ += "null";
break;
case intValue:

View File

@@ -1497,6 +1497,36 @@ JSONTEST_FIXTURE(ValueTest, typeChecksThrowExceptions) {
#endif
}
JSONTEST_FIXTURE(ValueTest, offsetAccessors) {
Json::Value x;
JSONTEST_ASSERT(x.getOffsetStart() == 0);
JSONTEST_ASSERT(x.getOffsetLimit() == 0);
x.setOffsetStart(10);
x.setOffsetLimit(20);
JSONTEST_ASSERT(x.getOffsetStart() == 10);
JSONTEST_ASSERT(x.getOffsetLimit() == 20);
Json::Value y(x);
JSONTEST_ASSERT(y.getOffsetStart() == 10);
JSONTEST_ASSERT(y.getOffsetLimit() == 20);
Json::Value z;
z.swap(y);
JSONTEST_ASSERT(z.getOffsetStart() == 10);
JSONTEST_ASSERT(z.getOffsetLimit() == 20);
JSONTEST_ASSERT(y.getOffsetStart() == 0);
JSONTEST_ASSERT(y.getOffsetLimit() == 0);
}
struct WriterTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) {
Json::FastWriter writer;
Json::Value nullValue;
JSONTEST_ASSERT(writer.write(nullValue) == "null\n");
writer.dropNullPlaceholders();
JSONTEST_ASSERT(writer.write(nullValue) == "\n");
}
struct StreamWriterTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(StreamWriterTest, dropNullPlaceholders) {
@@ -1516,6 +1546,7 @@ JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) {
bool ok = reader.parse("{ \"property\" : \"value\" }", root);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(reader.getFormattedErrorMessages().size() == 0);
JSONTEST_ASSERT(reader.getStructuredErrors().size() == 0);
}
JSONTEST_FIXTURE(ReaderTest, parseWithNoErrorsTestingOffsets) {
@@ -1527,6 +1558,25 @@ JSONTEST_FIXTURE(ReaderTest, parseWithNoErrorsTestingOffsets) {
root);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(reader.getFormattedErrorMessages().size() == 0);
JSONTEST_ASSERT(reader.getStructuredErrors().size() == 0);
JSONTEST_ASSERT(root["property"].getOffsetStart() == 15);
JSONTEST_ASSERT(root["property"].getOffsetLimit() == 34);
JSONTEST_ASSERT(root["property"][0].getOffsetStart() == 16);
JSONTEST_ASSERT(root["property"][0].getOffsetLimit() == 23);
JSONTEST_ASSERT(root["property"][1].getOffsetStart() == 25);
JSONTEST_ASSERT(root["property"][1].getOffsetLimit() == 33);
JSONTEST_ASSERT(root["obj"].getOffsetStart() == 44);
JSONTEST_ASSERT(root["obj"].getOffsetLimit() == 76);
JSONTEST_ASSERT(root["obj"]["nested"].getOffsetStart() == 57);
JSONTEST_ASSERT(root["obj"]["nested"].getOffsetLimit() == 60);
JSONTEST_ASSERT(root["obj"]["bool"].getOffsetStart() == 71);
JSONTEST_ASSERT(root["obj"]["bool"].getOffsetLimit() == 75);
JSONTEST_ASSERT(root["null"].getOffsetStart() == 87);
JSONTEST_ASSERT(root["null"].getOffsetLimit() == 91);
JSONTEST_ASSERT(root["false"].getOffsetStart() == 103);
JSONTEST_ASSERT(root["false"].getOffsetLimit() == 108);
JSONTEST_ASSERT(root.getOffsetStart() == 0);
JSONTEST_ASSERT(root.getOffsetLimit() == 110);
}
JSONTEST_FIXTURE(ReaderTest, parseWithOneError) {
@@ -1537,6 +1587,13 @@ JSONTEST_FIXTURE(ReaderTest, parseWithOneError) {
JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
"* Line 1, Column 15\n Syntax error: value, object or array "
"expected.\n");
std::vector<Json::Reader::StructuredError> errors =
reader.getStructuredErrors();
JSONTEST_ASSERT(errors.size() == 1);
JSONTEST_ASSERT(errors.at(0).offset_start == 14);
JSONTEST_ASSERT(errors.at(0).offset_limit == 15);
JSONTEST_ASSERT(errors.at(0).message ==
"Syntax error: value, object or array expected.");
}
JSONTEST_FIXTURE(ReaderTest, parseChineseWithOneError) {
@@ -1547,6 +1604,13 @@ JSONTEST_FIXTURE(ReaderTest, parseChineseWithOneError) {
JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
"* Line 1, Column 19\n Syntax error: value, object or array "
"expected.\n");
std::vector<Json::Reader::StructuredError> errors =
reader.getStructuredErrors();
JSONTEST_ASSERT(errors.size() == 1);
JSONTEST_ASSERT(errors.at(0).offset_start == 18);
JSONTEST_ASSERT(errors.at(0).offset_limit == 19);
JSONTEST_ASSERT(errors.at(0).message ==
"Syntax error: value, object or array expected.");
}
JSONTEST_FIXTURE(ReaderTest, parseWithDetailError) {
@@ -1557,6 +1621,12 @@ JSONTEST_FIXTURE(ReaderTest, parseWithDetailError) {
JSONTEST_ASSERT(reader.getFormattedErrorMessages() ==
"* Line 1, Column 16\n Bad escape sequence in string\nSee "
"Line 1, Column 20 for detail.\n");
std::vector<Json::Reader::StructuredError> errors =
reader.getStructuredErrors();
JSONTEST_ASSERT(errors.size() == 1);
JSONTEST_ASSERT(errors.at(0).offset_start == 15);
JSONTEST_ASSERT(errors.at(0).offset_limit == 23);
JSONTEST_ASSERT(errors.at(0).message == "Bad escape sequence in string");
}
struct CharReaderTest : JsonTest::TestCase {};
@@ -1791,6 +1861,175 @@ JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) {
JSONTEST_ASSERT_EQUAL(true, root.asBool());
delete reader;
}
struct CharReaderAllowDropNullTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(CharReaderAllowDropNullTest, issue178) {
Json::CharReaderBuilder b;
b.settings_["allowDroppedNullPlaceholders"] = true;
Json::Value root;
std::string errs;
Json::CharReader* reader(b.newCharReader());
{
char const doc[] = "{\"a\":,\"b\":true}";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
JSONTEST_ASSERT_EQUAL(Json::nullValue, root.get("a", true));
}
{
char const doc[] = "{\"a\":}";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(1u, root.size());
JSONTEST_ASSERT_EQUAL(Json::nullValue, root.get("a", true));
}
{
char const doc[] = "[]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(0u, root.size());
JSONTEST_ASSERT_EQUAL(Json::arrayValue, root);
}
{
char const doc[] = "[null]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(1u, root.size());
}
{
char const doc[] = "[,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
}
{
char const doc[] = "[,,,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(4u, root.size());
}
{
char const doc[] = "[null,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(2u, root.size());
}
{
char const doc[] = "[,null]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(2u, root.size());
}
{
char const doc[] = "[,,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(3u, root.size());
}
{
char const doc[] = "[null,,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(3u, root.size());
}
{
char const doc[] = "[,null,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(3u, root.size());
}
{
char const doc[] = "[,,null]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(3u, root.size());
}
{
char const doc[] = "[[],,,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(4u, root.size());
JSONTEST_ASSERT_EQUAL(Json::arrayValue, root[0u]);
}
{
char const doc[] = "[,[],,]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT_STRING_EQUAL("", errs);
JSONTEST_ASSERT_EQUAL(4u, root.size());
JSONTEST_ASSERT_EQUAL(Json::arrayValue, root[1u]);
}
{
char const doc[] = "[,,,[]]";
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL(4u, root.size());
JSONTEST_ASSERT_EQUAL(Json::arrayValue, root[3u]);
}
delete reader;
}
struct IteratorTest : JsonTest::TestCase {};
JSONTEST_FIXTURE(IteratorTest, distance) {
Json::Value json;
json["k1"] = "a";
json["k2"] = "b";
int dist;
std::string str;
for (Json::ValueIterator it = json.begin(); it != json.end(); ++it) {
dist = it - json.begin();
str = it->asString().c_str();
}
JSONTEST_ASSERT_EQUAL(1, dist);
JSONTEST_ASSERT_STRING_EQUAL("b", str);
}
int main(int argc, const char* argv[]) {
JsonTest::Runner runner;
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
@@ -1811,8 +2050,12 @@ int main(int argc, const char* argv[]) {
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareArray);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareObject);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, compareType);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, offsetAccessors);
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, typeChecksThrowExceptions);
JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithNoErrors);
JSONTEST_REGISTER_FIXTURE(
runner, ReaderTest, parseWithNoErrorsTestingOffsets);
@@ -1834,7 +2077,9 @@ int main(int argc, const char* argv[]) {
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterArray);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterBool);
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowDropNullTest, issue178);
JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, distance);
return runner.runCommandLine(argc, argv);
}

View File

@@ -1 +1 @@
0.8.1
1.4.4