diff --git a/include/json/forwards.h b/include/json/forwards.h index ccfe09abf..03ac1a9a6 100644 --- a/include/json/forwards.h +++ b/include/json/forwards.h @@ -29,6 +29,8 @@ class Path; class PathArgument; class Value; class ValueIteratorBase; +class OrderedValueIterator; +class OrderedValueConstIterator; class ValueIterator; class ValueConstIterator; diff --git a/include/json/value.h b/include/json/value.h index fb88c1829..e88d0beba 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -12,7 +12,7 @@ #include #include #include - +#include #ifndef JSON_USE_CPPTL_SMALLMAP #include #else @@ -23,7 +23,7 @@ #endif //Conditional NORETURN attribute on the throw functions would: -// a) suppress false positives from static code analysis +// a) suppress false positives from static code analysis // b) possibly improve optimization opportunities. #if !defined(JSONCPP_NORETURN) # if defined(_MSC_VER) @@ -62,7 +62,7 @@ class JSON_API Exception : public std::exception { /** Exceptions which the user cannot easily avoid. * * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input - * + * * \remark derived from Json::Exception */ class JSON_API RuntimeError : public Exception { @@ -73,7 +73,7 @@ class JSON_API RuntimeError : public Exception { /** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. * * These are precondition-violations (user bugs) and internal errors (our bugs). - * + * * \remark derived from Json::Exception */ class JSON_API LogicError : public Exception { @@ -174,10 +174,14 @@ class JSON_API StaticString { */ class JSON_API Value { friend class ValueIteratorBase; + friend class OrderedValueIteratorBase; public: typedef std::vector Members; + typedef std::deque> OrderedMembers; typedef ValueIterator iterator; typedef ValueConstIterator const_iterator; + typedef OrderedValueIterator ordered_iterator; + typedef OrderedValueConstIterator const_ordered_iterator; typedef Json::UInt UInt; typedef Json::Int Int; #if defined(JSON_HAS_INT64) @@ -545,6 +549,7 @@ Json::Value obj_value(Json::objectValue); // {} /// \pre type() is objectValue or nullValue /// \post if type() was nullValue, it remains nullValue Members getMemberNames() const; + Members getMemberNamesOrdered() const; //# ifdef JSON_USE_CPPTL // EnumMemberNames enumMemberNames() const; @@ -567,6 +572,12 @@ Json::Value obj_value(Json::objectValue); // {} const_iterator begin() const; const_iterator end() const; + ordered_iterator beginOrdered(); + const_ordered_iterator beginOrdered() const; + + ordered_iterator endOrdered (); + const_ordered_iterator endOrdered() const; + iterator begin(); iterator end(); @@ -601,7 +612,10 @@ Json::Value obj_value(Json::objectValue); // {} // } //}; + OrderedMembers *order; + union ValueHolder { + LargestInt int_; LargestUInt uint_; double real_; @@ -683,6 +697,72 @@ class JSON_API Path { Args args_; }; +/** \brief base class for Ordered Value iterators. + * + */ +class JSON_API OrderedValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef OrderedValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + JSONCPP_STRING name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::OrderedMembers::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + OrderedValueIteratorBase(); + explicit OrderedValueIteratorBase(const Value::OrderedMembers::iterator& current); +}; + + /** \brief base class for Value iterators. * */ @@ -850,6 +930,109 @@ class JSON_API ValueIterator : public ValueIteratorBase { pointer operator->() const { return &deref(); } }; +/** \brief Insertion order Iterator for object and array value. + */ +class JSON_API OrderedValueIterator : public OrderedValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef OrderedValueIterator SelfType; + + OrderedValueIterator(); + OrderedValueIterator(const OrderedValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit OrderedValueIterator(const Value::OrderedMembers::iterator& current); +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API OrderedValueConstIterator : public OrderedValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + //typedef unsigned int size_t; + //typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef OrderedValueConstIterator SelfType; + + OrderedValueConstIterator(); + OrderedValueConstIterator(OrderedValueIterator const& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit OrderedValueConstIterator(const Value::OrderedMembers::iterator& current); +public: + SelfType& operator=(const OrderedValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + + + } // namespace Json diff --git a/include/json/writer.h b/include/json/writer.h index 2c1e65bf1..5d2ab1d4f 100644 --- a/include/json/writer.h +++ b/include/json/writer.h @@ -218,9 +218,11 @@ class JSON_API StyledWriter : public Writer { * \return String containing the JSON document that represents the root value. */ JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE; + JSONCPP_STRING writeOrdered(const Value& root); private: void writeValue(const Value& value); + void writeValueOrdered(const Value& value); void writeArrayValue(const Value& value); bool isMultineArray(const Value& value); void pushValue(const JSONCPP_STRING& value); diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index 2fe463095..2f25f2a04 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -344,6 +344,7 @@ bool Value::CZString::isStaticString() const { return storage_.policy_ == noDupl */ Value::Value(ValueType vtype) { initBasic(vtype); + switch (vtype) { case nullValue: break; @@ -359,7 +360,8 @@ Value::Value(ValueType vtype) { break; case arrayValue: case objectValue: - value_.map_ = new ObjectValues(); + value_.map_ = new ObjectValues(); + order = new OrderedMembers (); break; case booleanValue: value_.bool_ = false; @@ -456,6 +458,7 @@ Value::Value(Value const& other) break; case arrayValue: case objectValue: + order = new OrderedMembers (*other.order); value_.map_ = new ObjectValues(*other.value_.map_); break; default: @@ -481,7 +484,7 @@ Value::Value(Value&& other) { #endif Value::~Value() { - switch (type_) { + switch (type_) { case nullValue: case intValue: case uintValue: @@ -494,6 +497,7 @@ Value::~Value() { break; case arrayValue: case objectValue: + delete order; delete value_.map_; break; default: @@ -516,6 +520,7 @@ void Value::swapPayload(Value& other) { type_ = other.type_; other.type_ = temp; std::swap(value_, other.value_); + std::swap (order, other.order); int temp2 = allocated_; allocated_ = other.allocated_; other.allocated_ = temp2 & 0x1; @@ -1042,6 +1047,7 @@ Value& Value::resolveReference(const char* key) { ObjectValues::value_type defaultValue(actualKey, nullSingleton()); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; + order->push_back (std::make_pair(actualKey.data(), &value)); return value; } @@ -1062,6 +1068,7 @@ Value& Value::resolveReference(char const* key, char const* cend) ObjectValues::value_type defaultValue(actualKey, nullSingleton()); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; + order->push_back (std::make_pair(actualKey.data(), &value)); return value; } @@ -1147,7 +1154,18 @@ bool Value::removeMember(const char* key, const char* cend, Value* removed) if (it == value_.map_->end()) return false; *removed = it->second; + value_.map_->erase(it); + auto it2 = order->begin(); + for (; it2 != order->end (); it2++) + { + if (strcmp (it2->first.c_str(), key) == 0) + break; + } + + order->erase (it2); + + return true; } bool Value::removeMember(const char* key, Value* removed) @@ -1240,6 +1258,24 @@ Value::Members Value::getMemberNames() const { } return members; } + +Value::Members Value::getMemberNamesOrdered() 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()); + OrderedMembers::const_iterator it = order->begin(); + OrderedMembers::const_iterator itEnd = order->end(); + for (; it != itEnd; ++it) { + members.push_back(JSONCPP_STRING((*it).first.data(), + (*it).first.length())); + } + return members; +} + // //# ifdef JSON_USE_CPPTL // EnumMemberNames @@ -1443,6 +1479,30 @@ Value::iterator Value::begin() { return iterator(); } +Value::const_ordered_iterator Value::beginOrdered() const { + switch (type_) { + case arrayValue: + case objectValue: + return const_ordered_iterator(order->begin()); + break; + default: + break; + } + return const_ordered_iterator(); +} + +Value::ordered_iterator Value::beginOrdered() { + switch (type_) { + case arrayValue: + case objectValue: + return ordered_iterator(order->begin()); + break; + default: + break; + } + return ordered_iterator(); +} + Value::iterator Value::end() { switch (type_) { case arrayValue: @@ -1456,6 +1516,30 @@ Value::iterator Value::end() { return iterator(); } +Value::ordered_iterator Value::endOrdered() { + switch (type_) { + case arrayValue: + case objectValue: + return ordered_iterator(order->end()); + break; + default: + break; + } + return ordered_iterator(); +} + +Value::const_ordered_iterator Value::endOrdered() const { + switch (type_) { + case arrayValue: + case objectValue: + return const_ordered_iterator(order->end()); + break; + default: + break; + } + return const_ordered_iterator(); +} + // class PathArgument // ////////////////////////////////////////////////////////////////// diff --git a/src/lib_json/json_valueiterator.inl b/src/lib_json/json_valueiterator.inl index b45162b4a..7e9ec3a03 100644 --- a/src/lib_json/json_valueiterator.inl +++ b/src/lib_json/json_valueiterator.inl @@ -115,6 +115,127 @@ char const* ValueIteratorBase::memberName(char const** end) const { return cname; } +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class OrderedValueBaseIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + + +OrderedValueIteratorBase::OrderedValueIteratorBase() + : current_(), isNull_(true) { +} + +OrderedValueIteratorBase::OrderedValueIteratorBase( + const Value::OrderedMembers::iterator& current) + : current_(current), isNull_(false) {} + +Value& OrderedValueIteratorBase::deref() const { + return *(current_->second); +} + +void OrderedValueIteratorBase::increment() { + ++current_; +} + +void OrderedValueIteratorBase::decrement() { + //--current_; +} + +OrderedValueIteratorBase::difference_type +OrderedValueIteratorBase::computeDistance(const SelfType& other) const { +#ifdef JSON_USE_CPPTL_SMALLMAP + return other.current_ - current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::OrderedMembers::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +#endif +} + +bool OrderedValueIteratorBase::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +} + +void OrderedValueIteratorBase::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +Value OrderedValueIteratorBase::key() const { + return Value(1); +} + +UInt OrderedValueIteratorBase::index() const { + return Value::UInt(-1); +} + +JSONCPP_STRING OrderedValueIteratorBase::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) return JSONCPP_STRING(); + return JSONCPP_STRING(keey, end); +} + +char const* OrderedValueIteratorBase::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +char const* OrderedValueIteratorBase::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = NULL; + return NULL; + } + *end = cname + (*current_).first.length(); + return cname; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class OrderedValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +OrderedValueConstIterator::OrderedValueConstIterator() {} + +OrderedValueConstIterator::OrderedValueConstIterator(const Value::OrderedMembers::iterator& current) + : OrderedValueIteratorBase(current) {} + +OrderedValueConstIterator::OrderedValueConstIterator( OrderedValueIterator const& other) + : OrderedValueIteratorBase(other) {} + +OrderedValueConstIterator& OrderedValueConstIterator::operator=(const OrderedValueIteratorBase& other) { + copy(other); + return *this; +} + + // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -164,4 +285,27 @@ ValueIterator& ValueIterator::operator=(const SelfType& other) { return *this; } +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class OrderedValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +OrderedValueIterator::OrderedValueIterator() {} + +OrderedValueIterator::OrderedValueIterator(const Value::OrderedMembers::iterator& current) + : OrderedValueIteratorBase(current) {} + + +OrderedValueIterator::OrderedValueIterator(const OrderedValueIterator& other) + : OrderedValueIteratorBase(other) {} + +OrderedValueIterator& OrderedValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + + } // namespace Json diff --git a/src/lib_json/json_writer.cpp b/src/lib_json/json_writer.cpp index 35f0b1f08..7b4568c35 100644 --- a/src/lib_json/json_writer.cpp +++ b/src/lib_json/json_writer.cpp @@ -396,6 +396,17 @@ void FastWriter::writeValue(const Value& value) { StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3), addChildValues_() {} +JSONCPP_STRING StyledWriter::writeOrdered(const Value& root) { + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValueOrdered(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + JSONCPP_STRING StyledWriter::write(const Value& root) { document_ = ""; addChildValues_ = false; @@ -407,6 +418,67 @@ JSONCPP_STRING StyledWriter::write(const Value& root) { return document_; } +void StyledWriter::writeValueOrdered(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNamesOrdered()); + if (members.empty ()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const JSONCPP_STRING& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + + + void StyledWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: