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