// // Object.h // // Library: JSON // Package: JSON // Module: Object // // Definition of the Object class. // // Copyright (c) 2012, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #ifndef JSON_Object_INCLUDED #define JSON_Object_INCLUDED #include #include #include #include #include #include "Poco/Dynamic/Struct.h" #include "Poco/Dynamic/Var.h" #include "Poco/JSON/Array.h" #include "Poco/JSON/JSON.h" #include "Poco/JSON/Stringifier.h" #include "Poco/JSONString.h" #include "Poco/Nullable.h" #include "Poco/SharedPtr.h" namespace Poco { namespace JSON { class JSON_API Object /// Represents a JSON object. Object provides a representation based on /// shared pointers and optimized for performance. It is possible to /// convert Object to DynamicStruct. Conversion requires copying and therefore /// has performance penalty; the benefit is in improved syntax, eg: /// /// std::string json = "{ \"test\" : { \"property\" : \"value\" } }"; /// Parser parser; /// Var result = parser.parse(json); /// /// // use pointers to avoid copying /// Object::Ptr object = result.extract(); /// Var test = object->get("test"); // holds { "property" : "value" } /// Object::Ptr subObject = test.extract(); /// test = subObject->get("property"); /// std::string val = test.toString(); // val holds "value" /// /// // copy/convert to Poco::DynamicStruct /// Poco::DynamicStruct ds = *object; /// val = ds["test"]["property"]; // val holds "value" /// { public: typedef SharedPtr Ptr; typedef std::map ValueMap; typedef ValueMap::value_type ValueType; typedef ValueMap::iterator Iterator; typedef ValueMap::const_iterator ConstIterator; typedef std::vector NameList; explicit Object(int options = 0); /// Creates an empty Object. /// /// If JSON_PRESERVE_KEY_ORDER is specified, the object will /// preserve the items insertion order. Otherwise, items will be /// sorted by keys. /// /// If JSON_ESCAPE_UNICODE is specified, when the object is /// stringified, all unicode characters will be escaped in the /// resulting string. Object(const Object & copy); /// Creates an Object by copying another one. /// /// Struct is not copied to keep the operation as /// efficient as possible (when needed, it will be generated upon request). #ifdef POCO_ENABLE_CPP11 Object(Object && other); /// Move constructor Object & operator=(Object && other); // Move assignment operator #endif // POCO_ENABLE_CPP11 virtual ~Object(); /// Destroys the Object. Object & operator=(const Object & other); // Assignment operator void setEscapeUnicode(bool escape = true); /// Sets the flag for escaping unicode. bool getEscapeUnicode() const; /// Returns the flag for escaping unicode. Iterator begin(); /// Returns begin iterator for values. ConstIterator begin() const; /// Returns const begin iterator for values. Iterator end(); /// Returns end iterator for values. ConstIterator end() const; /// Returns const end iterator for values. Dynamic::Var get(const std::string & key) const; /// Retrieves a property. An empty value is /// returned when the property doesn't exist. Array::Ptr getArray(const std::string & key) const; /// Returns a SharedPtr to an array when the property /// is an array. An empty SharedPtr is returned when /// the element doesn't exist or is not an array. Object::Ptr getObject(const std::string & key) const; /// Returns a SharedPtr to an object when the property /// is an object. An empty SharedPtr is returned when /// the property doesn't exist or is not an object template T getValue(const std::string & key) const /// Retrieves the property with the given name and will /// try to convert the value to the given template type. /// The convert() method of Var is called /// which can also throw exceptions for invalid values. /// Note: This will not work for an array or an object. { Dynamic::Var value = get(key); return value.convert(); } template Poco::Nullable getNullableValue(const std::string & key) const /// Retrieves the property with the given name and will /// try to convert the value to the given template type. /// /// The convert method of Var is called /// which can also throw exceptions for invalid values. /// Note: This will not work for an array or an object. { if (isNull(key)) return Poco::Nullable(); Dynamic::Var value = get(key); return value.convert(); } void getNames(NameList & names) const; /// Fills the supplied vector with all property names. NameList getNames() const; /// Returns all property names. bool has(const std::string & key) const; /// Returns true when the given property exists. bool isArray(const std::string & key) const; /// Returns true when the given property contains an array. bool isArray(ConstIterator & it) const; /// Returns true when the given property contains an array. bool isNull(const std::string & key) const; /// Returns true when the given property contains a null value. bool isObject(const std::string & key) const; /// Returns true when the given property contains an object. bool isObject(ConstIterator & it) const; /// Returns true when the given property contains an object. template T optValue(const std::string & key, const T & def) const /// Returns the value of a property when the property exists /// and can be converted to the given type. Otherwise /// def will be returned. { T value = def; ValueMap::const_iterator it = _values.find(key); if (it != _values.end() && !it->second.isEmpty()) { try { value = it->second.convert(); } catch (...) { // The default value will be returned } } return value; } std::size_t size() const; /// Returns the number of properties. void set(const std::string & key, const Dynamic::Var & value); /// Sets a new value. void stringify(std::ostream & out, unsigned int indent = 0, int step = -1) const; /// Prints the object to out stream. /// /// When indent is 0, the object will be printed on a single /// line without indentation. void remove(const std::string & key); /// Removes the property with the given key. static Poco::DynamicStruct makeStruct(const Object::Ptr & obj); /// Utility function for creation of struct. operator const Poco::DynamicStruct &() const; /// Cast operator to Poco::DynamiStruct. void clear(); /// Clears the contents of the object. /// /// Insertion order preservation property is left intact. private: typedef std::deque KeyList; typedef Poco::DynamicStruct::Ptr StructPtr; void resetDynStruct() const; void syncKeys(const KeyList & keys); template void doStringify(const C & container, std::ostream & out, unsigned int indent, unsigned int step) const { int options = Poco::JSON_WRAP_STRINGS; options |= _escapeUnicode ? Poco::JSON_ESCAPE_UNICODE : 0; out << '{'; if (indent > 0) out << std::endl; typename C::const_iterator it = container.begin(); typename C::const_iterator end = container.end(); for (; it != end;) { for (unsigned int i = 0; i < indent; i++) out << ' '; Stringifier::stringify(getKey(it), out, indent, step, options); out << ((indent > 0) ? " : " : ":"); Stringifier::stringify(getValue(it), out, indent + step, step, options); if (++it != container.end()) out << ','; if (step > 0) out << std::endl; } if (indent >= step) indent -= step; for (unsigned int i = 0; i < indent; i++) out << ' '; out << '}'; } const std::string & getKey(ValueMap::const_iterator & it) const; const Dynamic::Var & getValue(ValueMap::const_iterator & it) const; const std::string & getKey(KeyList::const_iterator & it) const; const Dynamic::Var & getValue(KeyList::const_iterator & it) const; ValueMap _values; KeyList _keys; bool _preserveInsOrder; // Note: // The reason for this flag (rather than as argument to stringify()) is // because Object can be returned stringified from Dynamic::Var::toString(), // so it must know whether to escape unicode or not. bool _escapeUnicode; mutable StructPtr _pStruct; mutable bool _modified; }; // // inlines // inline void Object::setEscapeUnicode(bool escape) { _escapeUnicode = escape; } inline bool Object::getEscapeUnicode() const { return _escapeUnicode; } inline Object::Iterator Object::begin() { return _values.begin(); } inline Object::ConstIterator Object::begin() const { return _values.begin(); } inline Object::Iterator Object::end() { return _values.end(); } inline Object::ConstIterator Object::end() const { return _values.end(); } inline bool Object::has(const std::string & key) const { ValueMap::const_iterator it = _values.find(key); return it != _values.end(); } inline bool Object::isArray(const std::string & key) const { ValueMap::const_iterator it = _values.find(key); return isArray(it); } inline bool Object::isArray(ConstIterator & it) const { return it != _values.end() && (it->second.type() == typeid(Array::Ptr) || it->second.type() == typeid(Array)); } inline bool Object::isNull(const std::string & key) const { ValueMap::const_iterator it = _values.find(key); return it == _values.end() || it->second.isEmpty(); } inline bool Object::isObject(const std::string & key) const { ValueMap::const_iterator it = _values.find(key); return isObject(it); } inline bool Object::isObject(ConstIterator & it) const { return it != _values.end() && (it->second.type() == typeid(Object::Ptr) || it->second.type() == typeid(Object)); } inline std::size_t Object::size() const { return static_cast(_values.size()); } inline void Object::remove(const std::string & key) { _values.erase(key); if (_preserveInsOrder) { KeyList::iterator it = _keys.begin(); KeyList::iterator end = _keys.end(); for (; it != end; ++it) { if (key == (*it)->first) { _keys.erase(it); break; } } } _modified = true; } inline const std::string & Object::getKey(ValueMap::const_iterator & it) const { return it->first; } inline const Dynamic::Var & Object::getValue(ValueMap::const_iterator & it) const { return it->second; } inline const Dynamic::Var & Object::getValue(KeyList::const_iterator & it) const { ValueMap::const_iterator itv = _values.find((*it)->first); if (itv != _values.end()) return itv->second; else throw Poco::NotFoundException(); } } } // namespace Poco::JSON namespace Poco { namespace Dynamic { template <> class VarHolderImpl : public VarHolder { public: VarHolderImpl(const JSON::Object::Ptr & val) : _val(val) { } ~VarHolderImpl() { } const std::type_info & type() const { return typeid(JSON::Object::Ptr); } void convert(Int8 &) const { throw BadCastException(); } void convert(Int16 &) const { throw BadCastException(); } void convert(Int32 &) const { throw BadCastException(); } void convert(Int64 &) const { throw BadCastException(); } void convert(UInt8 &) const { throw BadCastException(); } void convert(UInt16 &) const { throw BadCastException(); } void convert(UInt32 &) const { throw BadCastException(); } void convert(UInt64 &) const { throw BadCastException(); } void convert(bool & value) const { value = !_val.isNull() && _val->size() > 0; } void convert(float &) const { throw BadCastException(); } void convert(double &) const { throw BadCastException(); } void convert(char &) const { throw BadCastException(); } void convert(std::string & s) const { std::ostringstream oss; _val->stringify(oss, 2); s = oss.str(); } void convert(DateTime & /*val*/) const { //TODO: val = _val; throw NotImplementedException("Conversion not implemented: JSON:Object => DateTime"); } void convert(LocalDateTime & /*ldt*/) const { //TODO: ldt = _val.timestamp(); throw NotImplementedException("Conversion not implemented: JSON:Object => LocalDateTime"); } void convert(Timestamp & /*ts*/) const { //TODO: ts = _val.timestamp(); throw NotImplementedException("Conversion not implemented: JSON:Object => Timestamp"); } VarHolder * clone(Placeholder * pVarHolder = 0) const { return cloneHolder(pVarHolder, _val); } const JSON::Object::Ptr & value() const { return _val; } bool isArray() const { return false; } bool isInteger() const { return false; } bool isSigned() const { return false; } bool isNumeric() const { return false; } bool isString() const { return false; } private: JSON::Object::Ptr _val; }; template <> class VarHolderImpl : public VarHolder { public: VarHolderImpl(const JSON::Object & val) : _val(val) { } ~VarHolderImpl() { } const std::type_info & type() const { return typeid(JSON::Object); } void convert(Int8 &) const { throw BadCastException(); } void convert(Int16 &) const { throw BadCastException(); } void convert(Int32 &) const { throw BadCastException(); } void convert(Int64 &) const { throw BadCastException(); } void convert(UInt8 &) const { throw BadCastException(); } void convert(UInt16 &) const { throw BadCastException(); } void convert(UInt32 &) const { throw BadCastException(); } void convert(UInt64 &) const { throw BadCastException(); } void convert(bool & value) const { value = _val.size() > 0; } void convert(float &) const { throw BadCastException(); } void convert(double &) const { throw BadCastException(); } void convert(char &) const { throw BadCastException(); } void convert(std::string & s) const { std::ostringstream oss; _val.stringify(oss, 2); s = oss.str(); } void convert(DateTime & /*val*/) const { //TODO: val = _val; throw NotImplementedException("Conversion not implemented: JSON:Object => DateTime"); } void convert(LocalDateTime & /*ldt*/) const { //TODO: ldt = _val.timestamp(); throw NotImplementedException("Conversion not implemented: JSON:Object => LocalDateTime"); } void convert(Timestamp & /*ts*/) const { //TODO: ts = _val.timestamp(); throw NotImplementedException("Conversion not implemented: JSON:Object => Timestamp"); } VarHolder * clone(Placeholder * pVarHolder = 0) const { return cloneHolder(pVarHolder, _val); } const JSON::Object & value() const { return _val; } bool isArray() const { return false; } bool isInteger() const { return false; } bool isSigned() const { return false; } bool isNumeric() const { return false; } bool isString() const { return false; } private: JSON::Object _val; }; } } // namespace Poco::Dynamic #endif // JSON_Object_INCLUDED