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
36namespace Poco {
37namespace JSON {
38
39
40class 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{
62public:
63 typedef SharedPtr<Object> Ptr;
64 typedef std::map<std::string, Dynamic::Var> ValueMap;
65 typedef ValueMap::value_type ValueType;
66 typedef ValueMap::iterator Iterator;
67 typedef ValueMap::const_iterator ConstIterator;
68 typedef std::vector<std::string> NameList;
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);
88 /// Move constructor
89
90 virtual ~Object();
91 /// Destroys the Object.
92
93 Object &operator =(const Object &other);
94 // Assignment operator
95
96 Object &operator =(Object &&other);
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>
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>
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>
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#ifdef POCO_ENABLE_CPP11
224
225 static Poco::OrderedDynamicStruct makeOrderedStruct(const Object::Ptr& obj);
226 /// Utility function for creation of ordered struct.
227
228 operator const Poco::OrderedDynamicStruct& () const;
229 /// Cast operator to Poco::OrderedDynamiStruct.
230
231#endif // POCO_ENABLE_CPP11
232
233 operator const Poco::DynamicStruct& () const;
234 /// Cast operator to Poco::DynamiStruct.
235
236 void clear();
237 /// Clears the contents of the object.
238 ///
239 /// Insertion order preservation property is left intact.
240
241private:
242 typedef std::deque<ValueMap::const_iterator> KeyList;
243 typedef Poco::DynamicStruct::Ptr StructPtr;
244#ifdef POCO_ENABLE_CPP11
245 typedef Poco::OrderedDynamicStruct::Ptr OrdStructPtr;
246#endif // POCO_ENABLE_CPP11
247
248 void syncKeys(const KeyList& keys);
249
250 template <typename T>
251 void resetDynStruct(T& pStruct) const
252 {
253 if (!pStruct)
254 pStruct = new typename T::Type;
255 else
256 pStruct->clear();
257 }
258
259 template <typename C>
260 void doStringify(const C& container, std::ostream& out, unsigned int indent, unsigned int step) const
261 {
262 int options = Poco::JSON_WRAP_STRINGS;
263 options |= _escapeUnicode ? Poco::JSON_ESCAPE_UNICODE : 0;
264
265 out << '{';
266
267 if (indent > 0) out << std::endl;
268
269 typename C::const_iterator it = container.begin();
270 typename C::const_iterator end = container.end();
271 for (; it != end;)
272 {
273 for (unsigned int i = 0; i < indent; i++) out << ' ';
274
275 Stringifier::stringify(getKey(it), out, indent, step, options);
276 out << ((indent > 0) ? " : " : ":");
277
278 Stringifier::stringify(getValue(it), out, indent + step, step, options);
279
280 if (++it != container.end()) out << ',';
281
282 if (step > 0) out << std::endl;
283 }
284
285 if (indent >= step) indent -= step;
286
287 for (unsigned int i = 0; i < indent; i++) out << ' ';
288
289 out << '}';
290 }
291
292 template <typename S>
293 static S makeStructImpl(const Object::Ptr& obj)
294 {
295 S ds;
296
297 if (obj->_preserveInsOrder)
298 {
299 KeyList::const_iterator it = obj->_keys.begin();
300 KeyList::const_iterator end = obj->_keys.end();
301 for (; it != end; ++it)
302 {
303 if (obj->isObject((*it)->first))
304 {
305 Object::Ptr pObj = obj->getObject((*it)->first);
306 S str = makeStructImpl<S>(pObj);
307 ds.insert((*it)->first, str);
308 }
309 else if (obj->isArray((*it)->first))
310 {
311 Array::Ptr pArr = obj->getArray((*it)->first);
312 std::vector<Poco::Dynamic::Var> v = Poco::JSON::Array::makeArray(pArr);
313 ds.insert((*it)->first, v);
314 }
315 else
316 ds.insert((*it)->first, (*it)->second);
317 }
318 }
319 else
320 {
321 ConstIterator it = obj->begin();
322 ConstIterator end = obj->end();
323 for (; it != end; ++it)
324 {
325 if (obj->isObject(it))
326 {
327 Object::Ptr pObj = obj->getObject(it->first);
328 S str = makeStructImpl<S>(pObj);
329 ds.insert(it->first, str);
330 }
331 else if (obj->isArray(it))
332 {
333 Array::Ptr pArr = obj->getArray(it->first);
334 std::vector<Poco::Dynamic::Var> v = Poco::JSON::Array::makeArray(pArr);
335 ds.insert(it->first, v);
336 }
337 else
338 ds.insert(it->first, it->second);
339 }
340 }
341
342 return ds;
343 }
344
345 const std::string& getKey(ValueMap::const_iterator& it) const;
346 const Dynamic::Var& getValue(ValueMap::const_iterator& it) const;
347 const std::string& getKey(KeyList::const_iterator& it) const;
348 const Dynamic::Var& getValue(KeyList::const_iterator& it) const;
349
350 ValueMap _values;
351 KeyList _keys;
352 bool _preserveInsOrder;
353 // Note:
354 // The reason for this flag (rather than as argument to stringify()) is
355 // because Object can be returned stringified from Dynamic::Var::toString(),
356 // so it must know whether to escape unicode or not.
357 bool _escapeUnicode;
358 mutable StructPtr _pStruct;
359#ifdef POCO_ENABLE_CPP11
360 mutable OrdStructPtr _pOrdStruct;
361#endif // POCO_ENABLE_CPP11
362 mutable bool _modified;
363};
364
365
366//
367// inlines
368//
369
370inline void Object::setEscapeUnicode(bool escape)
371{
372 _escapeUnicode = escape;
373}
374
375
376inline bool Object::getEscapeUnicode() const
377{
378 return _escapeUnicode;
379}
380
381
382inline Object::Iterator Object::begin()
383{
384 return _values.begin();
385}
386
387
388inline Object::ConstIterator Object::begin() const
389{
390 return _values.begin();
391}
392
393
394inline Object::Iterator Object::end()
395{
396 return _values.end();
397}
398
399
400inline Object::ConstIterator Object::end() const
401{
402 return _values.end();
403}
404
405
406inline bool Object::has(const std::string& key) const
407{
408 ValueMap::const_iterator it = _values.find(key);
409 return it != _values.end();
410}
411
412
413inline bool Object::isArray(const std::string& key) const
414{
415 ValueMap::const_iterator it = _values.find(key);
416 return isArray(it);
417}
418
419
420inline bool Object::isArray(ConstIterator& it) const
421{
422 return it != _values.end() && (it->second.type() == typeid(Array::Ptr) || it->second.type() == typeid(Array));
423}
424
425
426inline bool Object::isNull(const std::string& key) const
427{
428 ValueMap::const_iterator it = _values.find(key);
429 return it == _values.end() || it->second.isEmpty();
430}
431
432
433inline bool Object::isObject(const std::string& key) const
434{
435 ValueMap::const_iterator it = _values.find(key);
436 return isObject(it);
437}
438
439
440inline bool Object::isObject(ConstIterator& it) const
441{
442 return it != _values.end() && (it->second.type() == typeid(Object::Ptr) || it->second.type() == typeid(Object));
443}
444
445
446inline std::size_t Object::size() const
447{
448 return static_cast<std::size_t>(_values.size());
449}
450
451
452inline void Object::remove(const std::string& key)
453{
454 _values.erase(key);
455 if (_preserveInsOrder)
456 {
457 KeyList::iterator it = _keys.begin();
458 KeyList::iterator end = _keys.end();
459 for (; it != end; ++it)
460 {
461 if (key == (*it)->first)
462 {
463 _keys.erase(it);
464 break;
465 }
466 }
467 }
468 _modified = true;
469}
470
471
472inline const std::string& Object::getKey(ValueMap::const_iterator& it) const
473{
474 return it->first;
475}
476
477
478inline const Dynamic::Var& Object::getValue(ValueMap::const_iterator& it) const
479{
480 return it->second;
481}
482
483
484inline const Dynamic::Var& Object::getValue(KeyList::const_iterator& it) const
485{
486 ValueMap::const_iterator itv = _values.find((*it)->first);
487 if (itv != _values.end())
488 return itv->second;
489 else
490 throw Poco::NotFoundException();
491}
492
493
494} } // namespace Poco::JSON
495
496
497namespace Poco {
498namespace Dynamic {
499
500
501template <>
502class VarHolderImpl<JSON::Object::Ptr>: public VarHolder
503{
504public:
505 VarHolderImpl(const JSON::Object::Ptr& val): _val(val)
506 {
507 }
508
509 ~VarHolderImpl()
510 {
511 }
512
513 const std::type_info& type() const
514 {
515 return typeid(JSON::Object::Ptr);
516 }
517
518 void convert(Int8&) const
519 {
520 throw BadCastException();
521 }
522
523 void convert(Int16&) const
524 {
525 throw BadCastException();
526 }
527
528 void convert(Int32&) const
529 {
530 throw BadCastException();
531 }
532
533 void convert(Int64&) const
534 {
535 throw BadCastException();
536 }
537
538 void convert(UInt8&) const
539 {
540 throw BadCastException();
541 }
542
543 void convert(UInt16&) const
544 {
545 throw BadCastException();
546 }
547
548 void convert(UInt32&) const
549 {
550 throw BadCastException();
551 }
552
553 void convert(UInt64&) const
554 {
555 throw BadCastException();
556 }
557
558 void convert(bool& value) const
559 {
560 value = !_val.isNull() && _val->size() > 0;
561 }
562
563 void convert(float&) const
564 {
565 throw BadCastException();
566 }
567
568 void convert(double&) const
569 {
570 throw BadCastException();
571 }
572
573 void convert(char&) const
574 {
575 throw BadCastException();
576 }
577
578 void convert(std::string& s) const
579 {
580 std::ostringstream oss;
581 _val->stringify(oss, 2);
582 s = oss.str();
583 }
584
585 void convert(DateTime& /*val*/) const
586 {
587 //TODO: val = _val;
588 throw NotImplementedException("Conversion not implemented: JSON:Object => DateTime");
589 }
590
591 void convert(LocalDateTime& /*ldt*/) const
592 {
593 //TODO: ldt = _val.timestamp();
594 throw NotImplementedException("Conversion not implemented: JSON:Object => LocalDateTime");
595 }
596
597 void convert(Timestamp& /*ts*/) const
598 {
599 //TODO: ts = _val.timestamp();
600 throw NotImplementedException("Conversion not implemented: JSON:Object => Timestamp");
601 }
602
603 VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
604 {
605 return cloneHolder(pVarHolder, _val);
606 }
607
608 const JSON::Object::Ptr& value() const
609 {
610 return _val;
611 }
612
613 bool isArray() const
614 {
615 return false;
616 }
617
618 bool isInteger() const
619 {
620 return false;
621 }
622
623 bool isSigned() const
624 {
625 return false;
626 }
627
628 bool isNumeric() const
629 {
630 return false;
631 }
632
633 bool isString() const
634 {
635 return false;
636 }
637
638private:
639 JSON::Object::Ptr _val;
640};
641
642
643template <>
644class VarHolderImpl<JSON::Object>: public VarHolder
645{
646public:
647 VarHolderImpl(const JSON::Object& val): _val(val)
648 {
649 }
650
651 ~VarHolderImpl()
652 {
653 }
654
655 const std::type_info& type() const
656 {
657 return typeid(JSON::Object);
658 }
659
660 void convert(Int8&) const
661 {
662 throw BadCastException();
663 }
664
665 void convert(Int16&) const
666 {
667 throw BadCastException();
668 }
669
670 void convert(Int32&) const
671 {
672 throw BadCastException();
673 }
674
675 void convert(Int64&) const
676 {
677 throw BadCastException();
678 }
679
680 void convert(UInt8&) const
681 {
682 throw BadCastException();
683 }
684
685 void convert(UInt16&) const
686 {
687 throw BadCastException();
688 }
689
690 void convert(UInt32&) const
691 {
692 throw BadCastException();
693 }
694
695 void convert(UInt64&) const
696 {
697 throw BadCastException();
698 }
699
700 void convert(bool& value) const
701 {
702 value = _val.size() > 0;
703 }
704
705 void convert(float&) const
706 {
707 throw BadCastException();
708 }
709
710 void convert(double&) const
711 {
712 throw BadCastException();
713 }
714
715 void convert(char&) const
716 {
717 throw BadCastException();
718 }
719
720 void convert(std::string& s) const
721 {
722 std::ostringstream oss;
723 _val.stringify(oss, 2);
724 s = oss.str();
725 }
726
727 void convert(DateTime& /*val*/) const
728 {
729 //TODO: val = _val;
730 throw NotImplementedException("Conversion not implemented: JSON:Object => DateTime");
731 }
732
733 void convert(LocalDateTime& /*ldt*/) const
734 {
735 //TODO: ldt = _val.timestamp();
736 throw NotImplementedException("Conversion not implemented: JSON:Object => LocalDateTime");
737 }
738
739 void convert(Timestamp& /*ts*/) const
740 {
741 //TODO: ts = _val.timestamp();
742 throw NotImplementedException("Conversion not implemented: JSON:Object => Timestamp");
743 }
744
745 VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
746 {
747 return cloneHolder(pVarHolder, _val);
748 }
749
750 const JSON::Object& value() const
751 {
752 return _val;
753 }
754
755 bool isArray() const
756 {
757 return false;
758 }
759
760 bool isInteger() const
761 {
762 return false;
763 }
764
765 bool isSigned() const
766 {
767 return false;
768 }
769
770 bool isNumeric() const
771 {
772 return false;
773 }
774
775 bool isString() const
776 {
777 return false;
778 }
779
780private:
781 JSON::Object _val;
782};
783
784
785} } // namespace Poco::Dynamic
786
787
788#endif // JSON_Object_INCLUDED
789