1 //
2 // Object.h
3 //
4 // Library: JSON
5 // Package: JSON
6 // Module:  Object
7 //
8 // Definition of the Object class.
9 //
10 // Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
11 // and Contributors.
12 //
13 // SPDX-License-Identifier:	BSL-1.0
14 //
15 
16 
17 #ifndef JSON_Object_INCLUDED
18 #define JSON_Object_INCLUDED
19 
20 
21 #include "Poco/JSON/JSON.h"
22 #include "Poco/JSON/Array.h"
23 #include "Poco/JSON/Stringifier.h"
24 #include "Poco/JSONString.h"
25 #include "Poco/SharedPtr.h"
26 #include "Poco/Dynamic/Var.h"
27 #include "Poco/Dynamic/Struct.h"
28 #include "Poco/Nullable.h"
29 #include <map>
30 #include <vector>
31 #include <deque>
32 #include <iostream>
33 #include <sstream>
34 
35 
36 namespace Poco {
37 namespace JSON {
38 
39 
40 class JSON_API Object
41 	/// Represents a JSON object. Object provides a representation based on
42 	/// shared pointers and optimized for performance. It is possible to
43 	/// convert Object to DynamicStruct. Conversion requires copying and therefore
44 	/// has performance penalty; the benefit is in improved syntax, eg:
45 	///
46 	///    std::string json = "{ \"test\" : { \"property\" : \"value\" } }";
47 	///    Parser parser;
48 	///    Var result = parser.parse(json);
49 	///
50 	///    // use pointers to avoid copying
51 	///    Object::Ptr object = result.extract<Object::Ptr>();
52 	///    Var test = object->get("test"); // holds { "property" : "value" }
53 	///    Object::Ptr subObject = test.extract<Object::Ptr>();
54 	///    test = subObject->get("property");
55 	///    std::string val = test.toString(); // val holds "value"
56 	///
57 	///    // copy/convert to Poco::DynamicStruct
58 	///    Poco::DynamicStruct ds = *object;
59 	///    val = ds["test"]["property"]; // val holds "value"
60 	///
61 {
62 public:
63 	using Ptr = SharedPtr<Object>;
64 	using ValueMap = std::map<std::string, Dynamic::Var>;
65 	using ValueType = ValueMap::value_type;
66 	using Iterator = ValueMap::iterator;
67 	using ConstIterator = ValueMap::const_iterator;
68 	using NameList = std::vector<std::string>;
69 
70 	explicit Object(int options = 0);
71 		/// Creates an empty Object.
72 		///
73 		/// If JSON_PRESERVE_KEY_ORDER is specified, the object will
74 		/// preserve the items insertion order. Otherwise, items will be
75 		/// sorted by keys.
76 		///
77 		/// If JSON_ESCAPE_UNICODE is specified, when the object is
78 		/// stringified, all unicode characters will be escaped in the
79 		/// resulting string.
80 
81 	Object(const Object& copy);
82 		/// Creates an Object by copying another one.
83 		///
84 		/// Struct is not copied to keep the operation as
85 		/// efficient as possible (when needed, it will be generated upon request).
86 
87 	Object(Object&& other) noexcept;
88 		/// Move constructor
89 
90 	~Object();
91 		/// Destroys the Object.
92 
93 	Object &operator = (const Object &other);
94 		// Assignment operator
95 
96 	Object &operator = (Object &&other) noexcept;
97 		// Move asignment operator
98 
99 	void setEscapeUnicode(bool escape = true);
100 		/// Sets the flag for escaping unicode.
101 
102 	bool getEscapeUnicode() const;
103 		/// Returns the flag for escaping unicode.
104 
105 	Iterator begin();
106 		/// Returns begin iterator for values.
107 
108 	ConstIterator begin() const;
109 		/// Returns const begin iterator for values.
110 
111 	Iterator end();
112 		/// Returns end iterator for values.
113 
114 	ConstIterator end() const;
115 		/// Returns const end iterator for values.
116 
117 	Dynamic::Var get(const std::string& key) const;
118 		/// Retrieves a property. An empty value is
119 		/// returned when the property doesn't exist.
120 
121 	Array::Ptr getArray(const std::string& key) const;
122 		/// Returns a SharedPtr to an array when the property
123 		/// is an array. An empty SharedPtr is returned when
124 		/// the element doesn't exist or is not an array.
125 
126 	Object::Ptr getObject(const std::string& key) const;
127 		/// Returns a SharedPtr to an object when the property
128 		/// is an object. An empty SharedPtr is returned when
129 		/// the property doesn't exist or is not an object
130 
131 	template<typename T>
getValue(const std::string & key)132 	T getValue(const std::string& key) const
133 		/// Retrieves the property with the given name and will
134 		/// try to convert the value to the given template type.
135 		/// The convert<T>() method of Var is called
136 		/// which can also throw exceptions for invalid values.
137 		/// Note: This will not work for an array or an object.
138 	{
139 		Dynamic::Var value = get(key);
140 		return value.convert<T>();
141 	}
142 
143 	template<typename T>
getNullableValue(const std::string & key)144 	Poco::Nullable<T> getNullableValue(const std::string& key) const
145 		/// Retrieves the property with the given name and will
146 		/// try to convert the value to the given template type.
147 		///
148 		/// The convert<T> method of Var is called
149 		/// which can also throw exceptions for invalid values.
150 		/// Note: This will not work for an array or an object.
151 	{
152 		if (isNull(key))
153 			return Poco::Nullable<T>();
154 
155 		Dynamic::Var value = get(key);
156 		return value.convert<T>();
157 	}
158 
159 	void getNames(NameList& names) const;
160 		/// Fills the supplied vector with all property names.
161 
162 	NameList getNames() const;
163 		/// Returns all property names.
164 
165 	bool has(const std::string& key) const;
166 		/// Returns true when the given property exists.
167 
168 	bool isArray(const std::string& key) const;
169 		/// Returns true when the given property contains an array.
170 
171 	bool isArray(ConstIterator& it) const;
172 		/// Returns true when the given property contains an array.
173 
174 	bool isNull(const std::string& key) const;
175 		/// Returns true when the given property contains a null value.
176 
177 	bool isObject(const std::string& key) const;
178 		/// Returns true when the given property contains an object.
179 
180 	bool isObject(ConstIterator& it) const;
181 		/// Returns true when the given property contains an object.
182 
183 	template<typename T>
optValue(const std::string & key,const T & def)184 	T optValue(const std::string& key, const T& def) const
185 		/// Returns the value of a property when the property exists
186 		/// and can be converted to the given type. Otherwise
187 		/// def will be returned.
188 	{
189 		T value = def;
190 		ValueMap::const_iterator it = _values.find(key);
191 		if (it != _values.end() && ! it->second.isEmpty())
192 		{
193 			try
194 			{
195 				value = it->second.convert<T>();
196 			}
197 			catch (...)
198 			{
199 				// The default value will be returned
200 			}
201 		}
202 		return value;
203 	}
204 
205 	std::size_t size() const;
206 		/// Returns the number of properties.
207 
208 	Object& set(const std::string& key, const Dynamic::Var& value);
209 		/// Sets a new value.
210 
211 	void stringify(std::ostream& out, unsigned int indent = 0, int step = -1) const;
212 		/// Prints the object to out stream.
213 		///
214 		/// When indent is 0, the object will be printed on a single
215 		/// line without indentation.
216 
217 	void remove(const std::string& key);
218 		/// Removes the property with the given key.
219 
220 	static Poco::DynamicStruct makeStruct(const Object::Ptr& obj);
221 		/// Utility function for creation of struct.
222 
223 	static Poco::OrderedDynamicStruct makeOrderedStruct(const Object::Ptr& obj);
224 		/// Utility function for creation of ordered struct.
225 
226 	operator const Poco::OrderedDynamicStruct& () const;
227 		/// Cast operator to Poco::OrderedDynamiStruct.
228 
229 	operator const Poco::DynamicStruct& () const;
230 		/// Cast operator to Poco::DynamiStruct.
231 
232 	void clear();
233 		/// Clears the contents of the object.
234 		///
235 		/// Insertion order preservation property is left intact.
236 
237 private:
238 	typedef std::deque<ValueMap::const_iterator>  KeyList;
239 	typedef Poco::DynamicStruct::Ptr              StructPtr;
240 	typedef Poco::OrderedDynamicStruct::Ptr       OrdStructPtr;
241 
242 	void syncKeys(const KeyList& keys);
243 
244 	template <typename T>
resetDynStruct(T & pStruct)245 	void resetDynStruct(T& pStruct) const
246 	{
247 		if (!pStruct)
248 			pStruct = new typename T::Type;
249 		else
250 			pStruct->clear();
251 	}
252 
253 	template <typename C>
doStringify(const C & container,std::ostream & out,unsigned int indent,unsigned int step)254 	void doStringify(const C& container, std::ostream& out, unsigned int indent, unsigned int step) const
255 	{
256 		int options = Poco::JSON_WRAP_STRINGS;
257 		options |= _escapeUnicode ? Poco::JSON_ESCAPE_UNICODE : 0;
258 
259 		out << '{';
260 
261 		if (indent > 0) out << std::endl;
262 
263 		typename C::const_iterator it = container.begin();
264 		typename C::const_iterator end = container.end();
265 		for (; it != end;)
266 		{
267 			for (unsigned int i = 0; i < indent; i++) out << ' ';
268 
269 			Stringifier::stringify(getKey(it), out, indent, step, options);
270 			out << ((indent > 0) ? " : " : ":");
271 
272 			Stringifier::stringify(getValue(it), out, indent + step, step, options);
273 
274 			if (++it != container.end()) out << ',';
275 
276 			if (step > 0) out << std::endl;
277 		}
278 
279 		if (indent >= step) indent -= step;
280 
281 		for (unsigned int i = 0; i < indent; i++) out << ' ';
282 
283 		out << '}';
284 	}
285 
286 	template <typename S>
makeStructImpl(const Object::Ptr & obj)287 	static S makeStructImpl(const Object::Ptr& obj)
288 	{
289 		S ds;
290 
291 		if (obj->_preserveInsOrder)
292 		{
293 			KeyList::const_iterator it = obj->_keys.begin();
294 			KeyList::const_iterator end = obj->_keys.end();
295 			for (; it != end; ++it)
296 			{
297 				if (obj->isObject((*it)->first))
298 				{
299 					Object::Ptr pObj = obj->getObject((*it)->first);
300 					S str = makeStructImpl<S>(pObj);
301 					ds.insert((*it)->first, str);
302 				}
303 				else if (obj->isArray((*it)->first))
304 				{
305 					Array::Ptr pArr = obj->getArray((*it)->first);
306 					std::vector<Poco::Dynamic::Var> v = Poco::JSON::Array::makeArray(pArr);
307 					ds.insert((*it)->first, v);
308 				}
309 				else
310 					ds.insert((*it)->first, (*it)->second);
311 			}
312 		}
313 		else
314 		{
315 			ConstIterator it = obj->begin();
316 			ConstIterator end = obj->end();
317 			for (; it != end; ++it)
318 			{
319 				if (obj->isObject(it))
320 				{
321 					Object::Ptr pObj = obj->getObject(it->first);
322 					S str = makeStructImpl<S>(pObj);
323 					ds.insert(it->first, str);
324 				}
325 				else if (obj->isArray(it))
326 				{
327 					Array::Ptr pArr = obj->getArray(it->first);
328 					std::vector<Poco::Dynamic::Var> v = Poco::JSON::Array::makeArray(pArr);
329 					ds.insert(it->first, v);
330 				}
331 				else
332 					ds.insert(it->first, it->second);
333 			}
334 		}
335 
336 		return ds;
337 	}
338 
339 	const std::string& getKey(ValueMap::const_iterator& it) const;
340 	const Dynamic::Var& getValue(ValueMap::const_iterator& it) const;
341 	const std::string& getKey(KeyList::const_iterator& it) const;
342 	const Dynamic::Var& getValue(KeyList::const_iterator& it) const;
343 
344 	ValueMap          _values;
345 	KeyList           _keys;
346 	bool              _preserveInsOrder;
347 	// Note:
348 	//  The reason for this flag (rather than as argument to stringify()) is
349 	//  because Object can be returned stringified from Dynamic::Var::toString(),
350 	//  so it must know whether to escape unicode or not.
351 	bool              _escapeUnicode;
352 	mutable StructPtr    _pStruct;
353 	mutable OrdStructPtr _pOrdStruct;
354 	mutable bool         _modified;
355 };
356 
357 
358 //
359 // inlines
360 //
361 
setEscapeUnicode(bool escape)362 inline void Object::setEscapeUnicode(bool escape)
363 {
364 	_escapeUnicode = escape;
365 }
366 
367 
getEscapeUnicode()368 inline bool Object::getEscapeUnicode() const
369 {
370 	return _escapeUnicode;
371 }
372 
373 
begin()374 inline Object::Iterator Object::begin()
375 {
376 	return _values.begin();
377 }
378 
379 
begin()380 inline Object::ConstIterator Object::begin() const
381 {
382 	return _values.begin();
383 }
384 
385 
end()386 inline Object::Iterator Object::end()
387 {
388 	return _values.end();
389 }
390 
391 
end()392 inline Object::ConstIterator Object::end() const
393 {
394 	return _values.end();
395 }
396 
397 
has(const std::string & key)398 inline bool Object::has(const std::string& key) const
399 {
400 	ValueMap::const_iterator it = _values.find(key);
401 	return it != _values.end();
402 }
403 
404 
isArray(const std::string & key)405 inline bool Object::isArray(const std::string& key) const
406 {
407 	ValueMap::const_iterator it = _values.find(key);
408 	return isArray(it);
409 }
410 
411 
isArray(ConstIterator & it)412 inline bool Object::isArray(ConstIterator& it) const
413 {
414 	return it != _values.end() && (it->second.type() == typeid(Array::Ptr) || it->second.type() == typeid(Array));
415 }
416 
417 
isNull(const std::string & key)418 inline bool Object::isNull(const std::string& key) const
419 {
420 	ValueMap::const_iterator it = _values.find(key);
421 	return it == _values.end() || it->second.isEmpty();
422 }
423 
424 
isObject(const std::string & key)425 inline bool Object::isObject(const std::string& key) const
426 {
427 	ValueMap::const_iterator it = _values.find(key);
428 	return isObject(it);
429 }
430 
431 
isObject(ConstIterator & it)432 inline bool Object::isObject(ConstIterator& it) const
433 {
434 	return it != _values.end() && (it->second.type() == typeid(Object::Ptr) || it->second.type() == typeid(Object));
435 }
436 
437 
size()438 inline std::size_t Object::size() const
439 {
440 	return static_cast<std::size_t>(_values.size());
441 }
442 
443 
remove(const std::string & key)444 inline void Object::remove(const std::string& key)
445 {
446 	_values.erase(key);
447 	if (_preserveInsOrder)
448 	{
449 		KeyList::iterator it = _keys.begin();
450 		KeyList::iterator end = _keys.end();
451 		for (; it != end; ++it)
452 		{
453 			if (key == (*it)->first)
454 			{
455 				_keys.erase(it);
456 				break;
457 			}
458 		}
459 	}
460 	_modified = true;
461 }
462 
463 
getKey(ValueMap::const_iterator & it)464 inline const std::string& Object::getKey(ValueMap::const_iterator& it) const
465 {
466 	return it->first;
467 }
468 
469 
getValue(ValueMap::const_iterator & it)470 inline const Dynamic::Var& Object::getValue(ValueMap::const_iterator& it) const
471 {
472 	return it->second;
473 }
474 
475 
getValue(KeyList::const_iterator & it)476 inline const Dynamic::Var& Object::getValue(KeyList::const_iterator& it) const
477 {
478 	ValueMap::const_iterator itv = _values.find((*it)->first);
479 	if (itv != _values.end())
480 		return itv->second;
481 	else
482 		throw Poco::NotFoundException();
483 }
484 
485 
486 } } // namespace Poco::JSON
487 
488 
489 namespace Poco {
490 namespace Dynamic {
491 
492 
493 template <>
494 class VarHolderImpl<JSON::Object::Ptr>: public VarHolder
495 {
496 public:
VarHolderImpl(const JSON::Object::Ptr & val)497 	VarHolderImpl(const JSON::Object::Ptr& val): _val(val)
498 	{
499 	}
500 
~VarHolderImpl()501 	~VarHolderImpl()
502 	{
503 	}
504 
type()505 	const std::type_info& type() const
506 	{
507 		return typeid(JSON::Object::Ptr);
508 	}
509 
convert(Int8 &)510 	void convert(Int8&) const
511 	{
512 		throw BadCastException();
513 	}
514 
convert(Int16 &)515 	void convert(Int16&) const
516 	{
517 		throw BadCastException();
518 	}
519 
convert(Int32 &)520 	void convert(Int32&) const
521 	{
522 		throw BadCastException();
523 	}
524 
convert(Int64 &)525 	void convert(Int64&) const
526 	{
527 		throw BadCastException();
528 	}
529 
convert(UInt8 &)530 	void convert(UInt8&) const
531 	{
532 		throw BadCastException();
533 	}
534 
convert(UInt16 &)535 	void convert(UInt16&) const
536 	{
537 		throw BadCastException();
538 	}
539 
convert(UInt32 &)540 	void convert(UInt32&) const
541 	{
542 		throw BadCastException();
543 	}
544 
convert(UInt64 &)545 	void convert(UInt64&) const
546 	{
547 		throw BadCastException();
548 	}
549 
convert(bool & value)550 	void convert(bool& value) const
551 	{
552 		value = !_val.isNull() && _val->size() > 0;
553 	}
554 
convert(float &)555 	void convert(float&) const
556 	{
557 		throw BadCastException();
558 	}
559 
convert(double &)560 	void convert(double&) const
561 	{
562 		throw BadCastException();
563 	}
564 
convert(char &)565 	void convert(char&) const
566 	{
567 		throw BadCastException();
568 	}
569 
convert(std::string & s)570 	void convert(std::string& s) const
571 	{
572 		std::ostringstream oss;
573 		_val->stringify(oss, 2);
574 		s = oss.str();
575 	}
576 
convert(DateTime &)577 	void convert(DateTime& /*val*/) const
578 	{
579 		//TODO: val = _val;
580 		throw NotImplementedException("Conversion not implemented: JSON:Object => DateTime");
581 	}
582 
convert(LocalDateTime &)583 	void convert(LocalDateTime& /*ldt*/) const
584 	{
585 		//TODO: ldt = _val.timestamp();
586 		throw NotImplementedException("Conversion not implemented: JSON:Object => LocalDateTime");
587 	}
588 
convert(Timestamp &)589 	void convert(Timestamp& /*ts*/) const
590 	{
591 		//TODO: ts = _val.timestamp();
592 		throw NotImplementedException("Conversion not implemented: JSON:Object => Timestamp");
593 	}
594 
595 	VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
596 	{
597 		return cloneHolder(pVarHolder, _val);
598 	}
599 
value()600 	const JSON::Object::Ptr& value() const
601 	{
602 		return _val;
603 	}
604 
isArray()605 	bool isArray() const
606 	{
607 		return false;
608 	}
609 
isInteger()610 	bool isInteger() const
611 	{
612 		return false;
613 	}
614 
isSigned()615 	bool isSigned() const
616 	{
617 		return false;
618 	}
619 
isNumeric()620 	bool isNumeric() const
621 	{
622 		return false;
623 	}
624 
isString()625 	bool isString() const
626 	{
627 		return false;
628 	}
629 
630 private:
631 	JSON::Object::Ptr _val;
632 };
633 
634 
635 template <>
636 class VarHolderImpl<JSON::Object>: public VarHolder
637 {
638 public:
VarHolderImpl(const JSON::Object & val)639 	VarHolderImpl(const JSON::Object& val): _val(val)
640 	{
641 	}
642 
~VarHolderImpl()643 	~VarHolderImpl()
644 	{
645 	}
646 
type()647 	const std::type_info& type() const
648 	{
649 		return typeid(JSON::Object);
650 	}
651 
convert(Int8 &)652 	void convert(Int8&) const
653 	{
654 		throw BadCastException();
655 	}
656 
convert(Int16 &)657 	void convert(Int16&) const
658 	{
659 		throw BadCastException();
660 	}
661 
convert(Int32 &)662 	void convert(Int32&) const
663 	{
664 		throw BadCastException();
665 	}
666 
convert(Int64 &)667 	void convert(Int64&) const
668 	{
669 		throw BadCastException();
670 	}
671 
convert(UInt8 &)672 	void convert(UInt8&) const
673 	{
674 		throw BadCastException();
675 	}
676 
convert(UInt16 &)677 	void convert(UInt16&) const
678 	{
679 		throw BadCastException();
680 	}
681 
convert(UInt32 &)682 	void convert(UInt32&) const
683 	{
684 		throw BadCastException();
685 	}
686 
convert(UInt64 &)687 	void convert(UInt64&) const
688 	{
689 		throw BadCastException();
690 	}
691 
convert(bool & value)692 	void convert(bool& value) const
693 	{
694 		value = _val.size() > 0;
695 	}
696 
convert(float &)697 	void convert(float&) const
698 	{
699 		throw BadCastException();
700 	}
701 
convert(double &)702 	void convert(double&) const
703 	{
704 		throw BadCastException();
705 	}
706 
convert(char &)707 	void convert(char&) const
708 	{
709 		throw BadCastException();
710 	}
711 
convert(std::string & s)712 	void convert(std::string& s) const
713 	{
714 		std::ostringstream oss;
715 		_val.stringify(oss, 2);
716 		s = oss.str();
717 	}
718 
convert(DateTime &)719 	void convert(DateTime& /*val*/) const
720 	{
721 		//TODO: val = _val;
722 		throw NotImplementedException("Conversion not implemented: JSON:Object => DateTime");
723 	}
724 
convert(LocalDateTime &)725 	void convert(LocalDateTime& /*ldt*/) const
726 	{
727 		//TODO: ldt = _val.timestamp();
728 		throw NotImplementedException("Conversion not implemented: JSON:Object => LocalDateTime");
729 	}
730 
convert(Timestamp &)731 	void convert(Timestamp& /*ts*/) const
732 	{
733 		//TODO: ts = _val.timestamp();
734 		throw NotImplementedException("Conversion not implemented: JSON:Object => Timestamp");
735 	}
736 
737 	VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
738 	{
739 		return cloneHolder(pVarHolder, _val);
740 	}
741 
value()742 	const JSON::Object& value() const
743 	{
744 		return _val;
745 	}
746 
isArray()747 	bool isArray() const
748 	{
749 		return false;
750 	}
751 
isInteger()752 	bool isInteger() const
753 	{
754 		return false;
755 	}
756 
isSigned()757 	bool isSigned() const
758 	{
759 		return false;
760 	}
761 
isNumeric()762 	bool isNumeric() const
763 	{
764 		return false;
765 	}
766 
isString()767 	bool isString() const
768 	{
769 		return false;
770 	}
771 
772 private:
773 	JSON::Object _val;
774 };
775 
776 
777 } } // namespace Poco::Dynamic
778 
779 
780 #endif // JSON_Object_INCLUDED
781