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 | 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 | |
241 | private: |
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 | |
370 | inline void Object::setEscapeUnicode(bool escape) |
371 | { |
372 | _escapeUnicode = escape; |
373 | } |
374 | |
375 | |
376 | inline bool Object::getEscapeUnicode() const |
377 | { |
378 | return _escapeUnicode; |
379 | } |
380 | |
381 | |
382 | inline Object::Iterator Object::begin() |
383 | { |
384 | return _values.begin(); |
385 | } |
386 | |
387 | |
388 | inline Object::ConstIterator Object::begin() const |
389 | { |
390 | return _values.begin(); |
391 | } |
392 | |
393 | |
394 | inline Object::Iterator Object::end() |
395 | { |
396 | return _values.end(); |
397 | } |
398 | |
399 | |
400 | inline Object::ConstIterator Object::end() const |
401 | { |
402 | return _values.end(); |
403 | } |
404 | |
405 | |
406 | inline 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 | |
413 | inline bool Object::isArray(const std::string& key) const |
414 | { |
415 | ValueMap::const_iterator it = _values.find(key); |
416 | return isArray(it); |
417 | } |
418 | |
419 | |
420 | inline 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 | |
426 | inline 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 | |
433 | inline bool Object::isObject(const std::string& key) const |
434 | { |
435 | ValueMap::const_iterator it = _values.find(key); |
436 | return isObject(it); |
437 | } |
438 | |
439 | |
440 | inline 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 | |
446 | inline std::size_t Object::size() const |
447 | { |
448 | return static_cast<std::size_t>(_values.size()); |
449 | } |
450 | |
451 | |
452 | inline 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 | |
472 | inline const std::string& Object::getKey(ValueMap::const_iterator& it) const |
473 | { |
474 | return it->first; |
475 | } |
476 | |
477 | |
478 | inline const Dynamic::Var& Object::getValue(ValueMap::const_iterator& it) const |
479 | { |
480 | return it->second; |
481 | } |
482 | |
483 | |
484 | inline 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 | |
497 | namespace Poco { |
498 | namespace Dynamic { |
499 | |
500 | |
501 | template <> |
502 | class VarHolderImpl<JSON::Object::Ptr>: public VarHolder |
503 | { |
504 | public: |
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 (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 | |
638 | private: |
639 | JSON::Object::Ptr _val; |
640 | }; |
641 | |
642 | |
643 | template <> |
644 | class VarHolderImpl<JSON::Object>: public VarHolder |
645 | { |
646 | public: |
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 (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 | |
780 | private: |
781 | JSON::Object _val; |
782 | }; |
783 | |
784 | |
785 | } } // namespace Poco::Dynamic |
786 | |
787 | |
788 | #endif // JSON_Object_INCLUDED |
789 | |