• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bblanchon / ArduinoJson / 17403571260

30 Aug 2025 07:55AM UTC coverage: 99.257% (+0.04%) from 99.218%
17403571260

push

github

bblanchon
VariantImpl: add `toArrayIfNull()` and `toObjectIfNull()`

14 of 14 new or added lines in 5 files covered. (100.0%)

5 existing lines in 3 files now uncovered.

3874 of 3903 relevant lines covered (99.26%)

11049.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.67
/src/ArduinoJson/Variant/VariantImpl.hpp
1
// ArduinoJson - https://arduinojson.org
2
// Copyright © 2014-2025, Benoit BLANCHON
3
// MIT License
4

5
#pragma once
6

7
#include <ArduinoJson/Memory/ResourceManager.hpp>
8
#include <ArduinoJson/Misc/SerializedValue.hpp>
9
#include <ArduinoJson/Numbers/convertNumber.hpp>
10
#include <ArduinoJson/Strings/JsonString.hpp>
11
#include <ArduinoJson/Strings/StringAdapters.hpp>
12
#include <ArduinoJson/Variant/VariantData.hpp>
13

14
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
15

16
class CollectionIterator;
17

18
class VariantImpl {
19
  VariantData* data_;
20
  ResourceManager* resources_;
21

22
 public:
23
  using iterator = CollectionIterator;
24

25
  VariantImpl() : data_(nullptr), resources_(nullptr) {}
5,343✔
26

27
  VariantImpl(VariantData* data, ResourceManager* resources)
383,766✔
28
      : data_(data), resources_(resources) {}
383,766✔
29

30
  VariantData* data() const {
297,615✔
31
    return data_;
297,615✔
32
  }
33

34
  ResourceManager* resources() const {
362,857✔
35
    return resources_;
362,857✔
36
  }
37

38
  template <typename TVisitor>
39
  typename TVisitor::result_type accept(TVisitor& visit) const {
139,445✔
40
    if (!data_)
139,445✔
41
      return visit.visit(nullptr);
168✔
42

43
#if ARDUINOJSON_USE_8_BYTE_POOL
44
    auto eightByteValue = getEightByte();
139,277✔
45
#endif
46
    switch (data_->type) {
139,277✔
47
      case VariantType::Float:
121✔
48
        return visit.visit(data_->content.asFloat);
121✔
49

50
#if ARDUINOJSON_USE_DOUBLE
51
      case VariantType::Double:
16✔
52
        return visit.visit(eightByteValue->asDouble);
16✔
53
#endif
54

55
      case VariantType::Array:
979✔
56
        return visit.visitArray(VariantImpl(data_, resources_));
979✔
57

58
      case VariantType::Object:
2,670✔
59
        return visit.visitObject(VariantImpl(data_, resources_));
2,670✔
60

61
      case VariantType::TinyString:
340✔
62
        return visit.visit(JsonString(data_->content.asTinyString));
340✔
63

64
      case VariantType::LongString: {
1,744✔
65
        auto s = asStringNode();
1,744✔
66
        return visit.visit(JsonString(s->data, s->length));
1,744✔
67
      }
68

69
      case VariantType::RawString: {
125✔
70
        auto s = asStringNode();
125✔
71
        return visit.visit(RawString(s->data, s->length));
125✔
72
      }
73

74
      case VariantType::Int32:
1,178✔
75
        return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
1,178✔
76

77
      case VariantType::Uint32:
255✔
78
        return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
255✔
79

80
#if ARDUINOJSON_USE_LONG_LONG
81
      case VariantType::Int64:
7✔
82
        return visit.visit(eightByteValue->asInt64);
7✔
83

84
      case VariantType::Uint64:
8✔
85
        return visit.visit(eightByteValue->asUint64);
8✔
86
#endif
87

88
      case VariantType::Boolean:
607✔
89
        return visit.visit(data_->content.asBoolean != 0);
607✔
90

91
      default:
131,227✔
92
        return visit.visit(nullptr);
131,227✔
93
    }
94
  }
95

96
  VariantData* addElement();
97
  void addElement(Slot<VariantData> slot);
98

99
  bool asBoolean() const {
1,254✔
100
    if (!data_)
1,254✔
101
      return false;
699✔
102

103
#if ARDUINOJSON_USE_8_BYTE_POOL
104
    auto eightByteValue = getEightByte();
555✔
105
#endif
106
    switch (data_->type) {
555✔
107
      case VariantType::Boolean:
305✔
108
        return data_->content.asBoolean;
305✔
109
      case VariantType::Uint32:
6✔
110
      case VariantType::Int32:
111
        return data_->content.asUint32 != 0;
6✔
112
      case VariantType::Float:
2✔
113
        return data_->content.asFloat != 0;
2✔
114
#if ARDUINOJSON_USE_DOUBLE
115
      case VariantType::Double:
4✔
116
        return eightByteValue->asDouble != 0;
4✔
117
#endif
118
      case VariantType::Null:
6✔
119
        return false;
6✔
120
#if ARDUINOJSON_USE_LONG_LONG
121
      case VariantType::Uint64:
2✔
122
      case VariantType::Int64:
123
        return eightByteValue->asUint64 != 0;
2✔
124
#endif
125
      default:
230✔
126
        return true;
230✔
127
    }
128
  }
129

130
  template <typename T>
131
  T asFloat() const {
55✔
132
    if (!data_)
55✔
133
      return 0.0;
1✔
134

135
    static_assert(is_floating_point<T>::value, "T must be a floating point");
136
#if ARDUINOJSON_USE_8_BYTE_POOL
137
    auto eightByteValue = getEightByte();
54✔
138
#endif
139
    const char* str = nullptr;
54✔
140
    switch (data_->type) {
54✔
141
      case VariantType::Boolean:
2✔
142
        return static_cast<T>(data_->content.asBoolean);
2✔
143
      case VariantType::Uint32:
2✔
144
        return static_cast<T>(data_->content.asUint32);
2✔
145
      case VariantType::Int32:
4✔
146
        return static_cast<T>(data_->content.asInt32);
4✔
147
#if ARDUINOJSON_USE_LONG_LONG
148
      case VariantType::Uint64:
1✔
149
        return static_cast<T>(eightByteValue->asUint64);
1✔
150
      case VariantType::Int64:
1✔
151
        return static_cast<T>(eightByteValue->asInt64);
1✔
152
#endif
153
      case VariantType::TinyString:
2✔
154
        str = data_->content.asTinyString;
2✔
155
        break;
2✔
156
      case VariantType::LongString:
1✔
157
        str = asStringNode()->data;
1✔
158
        break;
1✔
159
      case VariantType::Float:
18✔
160
        return static_cast<T>(data_->content.asFloat);
18✔
161
#if ARDUINOJSON_USE_DOUBLE
162
      case VariantType::Double:
21✔
163
        return static_cast<T>(eightByteValue->asDouble);
21✔
164
#endif
165
      default:
2✔
166
        return 0.0;
2✔
167
    }
168

169
    ARDUINOJSON_ASSERT(str != nullptr);
170
    return parseNumber<T>(str);
3✔
171
  }
172

173
  template <typename T>
174
  T asIntegral() const {
203✔
175
    if (!data_)
203✔
176
      return 0;
5✔
177

178
    static_assert(is_integral<T>::value, "T must be an integral type");
179
#if ARDUINOJSON_USE_8_BYTE_POOL
180
    auto eightByteValue = getEightByte();
198✔
181
#endif
182
    const char* str = nullptr;
198✔
183
    switch (data_->type) {
198✔
184
      case VariantType::Boolean:
2✔
185
        return data_->content.asBoolean;
2✔
186
      case VariantType::Uint32:
66✔
187
        return convertNumber<T>(data_->content.asUint32);
66✔
188
      case VariantType::Int32:
83✔
189
        return convertNumber<T>(data_->content.asInt32);
83✔
190
#if ARDUINOJSON_USE_LONG_LONG
191
      case VariantType::Uint64:
10✔
192
        return convertNumber<T>(eightByteValue->asUint64);
10✔
193
      case VariantType::Int64:
11✔
194
        return convertNumber<T>(eightByteValue->asInt64);
11✔
195
#endif
196
      case VariantType::TinyString:
3✔
197
        str = data_->content.asTinyString;
3✔
198
        break;
3✔
199
      case VariantType::LongString:
6✔
200
        str = asStringNode()->data;
6✔
201
        break;
6✔
202
      case VariantType::Float:
5✔
203
        return convertNumber<T>(data_->content.asFloat);
5✔
204
#if ARDUINOJSON_USE_DOUBLE
205
      case VariantType::Double:
10✔
206
        return convertNumber<T>(eightByteValue->asDouble);
10✔
207
#endif
208
      default:
2✔
209
        return 0;
2✔
210
    }
211

212
    ARDUINOJSON_ASSERT(str != nullptr);
213
    return parseNumber<T>(str);
9✔
214
  }
215

216
  JsonString asRawString() const {
54✔
217
    switch (type()) {
54✔
218
      case VariantType::RawString: {
30✔
219
        auto s = asStringNode();
30✔
220
        return JsonString(s->data, s->length);
30✔
221
      }
222
      default:
24✔
223
        return JsonString();
24✔
224
    }
225
  }
226

227
  JsonString asString() const {
12,816✔
228
    switch (type()) {
12,816✔
229
      case VariantType::TinyString:
2,791✔
230
        return JsonString(data_->content.asTinyString);
2,791✔
231
      case VariantType::LongString: {
9,583✔
232
        auto s = asStringNode();
9,583✔
233
        return JsonString(s->data, s->length);
9,583✔
234
      }
235
      default:
442✔
236
        return JsonString();
442✔
237
    }
238
  }
239

240
  StringNode* asStringNode() const {
11,534✔
241
    ARDUINOJSON_ASSERT(type() & VariantTypeBits::OwnedStringBit);
242
    return data_->content.asStringNode;
11,534✔
243
  }
244

245
#if ARDUINOJSON_USE_8_BYTE_POOL
246
  const EightByteValue* getEightByte() const {
140,232✔
247
    return type() & VariantTypeBits::EightByteBit
140,232✔
248
               ? resources_->getEightByte(data_->content.asSlotId)
140,232✔
249
               : 0;
140,232✔
250
  }
251
#endif
252

253
  iterator createIterator() const;
254

255
  VariantData* getElement(size_t index) const;
256

257
  VariantData* getOrAddElement(size_t index);
258

259
  void addMember(Slot<VariantData> key, Slot<VariantData> value);
260

261
  template <typename TAdaptedString>
262
  VariantData* addMember(TAdaptedString key);
263

264
  template <typename TAdaptedString>
265
  VariantData* getMember(TAdaptedString key) const;
266

267
  template <typename TAdaptedString>
268
  VariantData* getOrAddMember(TAdaptedString key);
269

270
  bool isArray() const {
70,232✔
271
    return type() == VariantType::Array;
70,232✔
272
  }
273

274
  bool isBoolean() const {
275
    return type() == VariantType::Boolean;
276
  }
277

278
  bool isFloat() const {
48✔
279
    return data_ && data_->isFloat();
48✔
280
  }
281

282
  template <typename T>
283
  bool isInteger() const {
167✔
284
    if (!data_)
167✔
285
      return false;
16✔
286

287
#if ARDUINOJSON_USE_LONG_LONG
288
    auto eightByteValue = getEightByte();
148✔
289
#endif
290
    switch (data_->type) {
151✔
291
      case VariantType::Uint32:
11✔
292
        return canConvertNumber<T>(data_->content.asUint32);
11✔
293

294
      case VariantType::Int32:
65✔
295
        return canConvertNumber<T>(data_->content.asInt32);
65✔
296

297
#if ARDUINOJSON_USE_LONG_LONG
298
      case VariantType::Uint64:
3✔
299
        return canConvertNumber<T>(eightByteValue->asUint64);
3✔
300

301
      case VariantType::Int64:
4✔
302
        return canConvertNumber<T>(eightByteValue->asInt64);
4✔
303
#endif
304

305
      default:
68✔
306
        return false;
68✔
307
    }
308
  }
309

310
  bool isUnbound() const {
301,316✔
311
    return !data_;
301,316✔
312
  }
313

314
  bool isNull() const {
2,893✔
315
    return type() == VariantType::Null;
2,893✔
316
  }
317

318
  bool isObject() const {
5,685✔
319
    return type() == VariantType::Object;
5,685✔
320
  }
321

322
  bool isString() const {
96✔
323
    return data_ && data_->isString();
96✔
324
  }
325

326
  size_t nesting() const;
327

328
  void removeElement(iterator it);
329

330
  void removeElement(size_t index);
331

332
  template <typename TAdaptedString>
333
  void removeMember(TAdaptedString key);
334

335
  void removeMember(iterator it);
336

337
  bool copyVariant(const VariantImpl& src) {
92✔
338
    switch (src.type()) {
92✔
339
      case VariantType::Null:
3✔
340
        return true;
3✔
341

342
      case VariantType::Array:
3✔
343
        return copyArray(src);
3✔
344

345
      case VariantType::Object:
9✔
346
        return copyObject(src);
9✔
347

348
      case VariantType::RawString:
4✔
349
        return setRawString(adaptString(src.asRawString()));
4✔
350

351
      case VariantType::LongString:
64✔
352
        return setLongString(adaptString(src.asString()));
64✔
353

354
      default:
9✔
355
        data_->content = src.data_->content;
9✔
356
        data_->type = src.data_->type;
9✔
357
        return true;
9✔
358
    }
359
  }
360

361
  bool copyArray(const VariantImpl& src);
362
  bool copyObject(const VariantImpl& src);
363

364
  bool setBoolean(bool value) {
250✔
365
    if (!data_)
250✔
366
      return false;
2✔
367
    data_->setBoolean(value);
248✔
368
    return true;
248✔
369
  }
370

371
  template <typename T>
372
  enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
90✔
373
    ARDUINOJSON_ASSERT(type() == VariantType::Null);  // must call clear() first
374
    if (!data_)
90✔
UNCOV
375
      return false;
×
376
    data_->type = VariantType::Float;
90✔
377
    data_->content.asFloat = value;
90✔
378
    return true;
90✔
379
  }
380

381
  template <typename T>
382
  enable_if_t<sizeof(T) == 8, bool> setFloat(T value) {
80✔
383
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
384

385
    if (!data_)
80✔
386
      return false;
1✔
387

388
    float valueAsFloat = static_cast<float>(value);
79✔
389

390
#if ARDUINOJSON_USE_DOUBLE
391
    if (value == valueAsFloat) {
77✔
392
      data_->type = VariantType::Float;
32✔
393
      data_->content.asFloat = valueAsFloat;
32✔
394
    } else {
395
      auto slot = resources_->allocEightByte();
45✔
396
      if (!slot)
45✔
397
        return false;
3✔
398
      data_->type = VariantType::Double;
42✔
399
      data_->content.asSlotId = slot.id();
42✔
400
      slot->asDouble = value;
42✔
401
    }
402
#else
403
    data_->type = VariantType::Float;
2✔
404
    data_->content.asFloat = valueAsFloat;
2✔
405
#endif
406
    return true;
76✔
407
  }
408

409
  template <typename T>
410
  enable_if_t<is_signed<T>::value, bool> setInteger(T value) {
1,557✔
411
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
412

413
    if (!data_)
1,557✔
414
      return false;
15✔
415

416
    if (canConvertNumber<int32_t>(value)) {
1,542✔
417
      data_->type = VariantType::Int32;
1,525✔
418
      data_->content.asInt32 = static_cast<int32_t>(value);
1,525✔
419
    }
420
#if ARDUINOJSON_USE_LONG_LONG
421
    else {
422
      auto slot = resources_->allocEightByte();
17✔
423
      if (!slot)
17✔
424
        return false;
3✔
425
      data_->type = VariantType::Int64;
14✔
426
      data_->content.asSlotId = slot.id();
14✔
427
      slot->asInt64 = value;
14✔
428
    }
429
#endif
430
    return true;
1,539✔
431
  }
432

433
  template <typename T>
434
  enable_if_t<is_unsigned<T>::value, bool> setInteger(T value) {
2,299✔
435
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
436

437
    if (!data_)
2,299✔
438
      return false;
1✔
439

440
    if (canConvertNumber<uint32_t>(value)) {
2,298✔
441
      data_->type = VariantType::Uint32;
2,281✔
442
      data_->content.asUint32 = static_cast<uint32_t>(value);
2,281✔
443
    }
444
#if ARDUINOJSON_USE_LONG_LONG
445
    else {
446
      auto slot = resources_->allocEightByte();
16✔
447
      if (!slot)
16✔
448
        return false;
3✔
449
      data_->type = VariantType::Uint64;
13✔
450
      data_->content.asSlotId = slot.id();
13✔
451
      slot->asUint64 = value;
13✔
452
    }
453
#endif
454
    return true;
2,295✔
455
  }
456

457
  template <typename TAdaptedString>
458
  bool setRawString(TAdaptedString value) {
36✔
459
    if (!data_)
36✔
460
      return false;
2✔
461
    auto dup = resources_->saveString(value);
34✔
462
    if (!dup)
34✔
463
      return false;
4✔
464
    data_->setRawString(dup);
30✔
465
    return true;
30✔
466
  }
467

468
  template <typename TAdaptedString>
469
  bool setString(TAdaptedString value) {
66,964✔
470
    ARDUINOJSON_ASSERT(isNull());  // must call clear() first
471

472
    if (!data_)
66,964✔
473
      return false;
5✔
474

475
    if (value.isNull())
66,959✔
476
      return true;
65,552✔
477

478
    if (isTinyString(value, value.size())) {
1,407✔
479
      data_->setTinyString(value);
410✔
480
      return true;
410✔
481
    }
482

483
    return setLongString(value);
997✔
484
  }
485

486
  template <typename TAdaptedString>
487
  bool setLongString(TAdaptedString value) {
1,061✔
488
    auto dup = resources_->saveString(value);
1,061✔
489
    if (!dup)
1,061✔
490
      return false;
12✔
491

492
    data_->setLongString(dup);
1,049✔
493
    return true;
1,049✔
494
  }
495

496
  void toArrayIfNull() {
456✔
497
    if (data_ && data_->type == VariantType::Null)
456✔
498
      data_->toArray();
246✔
499
  }
456✔
500

501
  void toObjectIfNull() {
924✔
502
    if (data_ && data_->type == VariantType::Null)
924✔
503
      data_->toObject();
366✔
504
  }
924✔
505

506
  void empty() {
54✔
507
    auto coll = getCollectionData();
54✔
508

509
    auto next = coll->head;
54✔
510
    while (next != NULL_SLOT) {
342✔
511
      auto currId = next;
288✔
512
      auto slot = getVariant(next);
288✔
513
      next = slot->next;
288✔
514
      freeVariant({slot, currId});
288✔
515
    }
516

517
    coll->head = NULL_SLOT;
54✔
518
    coll->tail = NULL_SLOT;
54✔
519
  }
54✔
520

521
  size_t size() const;
522

523
  VariantType type() const {
232,491✔
524
    return data_ ? data_->type : VariantType::Null;
232,491✔
525
  }
526

527
  // Release the resources used by this variant and set it to null.
528
  void clear() {
69,414✔
529
    if (!data_)
69,414✔
530
      return;
27✔
531

532
    if (data_->type & VariantTypeBits::OwnedStringBit)
69,387✔
533
      resources_->dereferenceString(asStringNode());
45✔
534

535
#if ARDUINOJSON_USE_8_BYTE_POOL
536
    if (data_->type & VariantTypeBits::EightByteBit)
69,387✔
537
      resources_->freeEightByte(data_->content.asSlotId);
3✔
538
#endif
539

540
    if (data_->type & VariantTypeBits::CollectionMask)
69,387✔
541
      empty();
50✔
542

543
    data_->type = VariantType::Null;
69,387✔
544
  }
545

546
 private:
547
  template <typename TAdaptedString>
548
  iterator findKey(TAdaptedString key) const;
549

550
  iterator at(size_t index) const;
551

552
  void removeOne(iterator it);
553
  void removePair(iterator it);
554

555
  VariantData* getVariant(SlotId id) const {
70,038✔
556
    ARDUINOJSON_ASSERT(resources_ != nullptr);
557
    return resources_->getVariant(id);
70,038✔
558
  }
559

560
  void freeVariant(Slot<VariantData> slot) {
366✔
561
    ARDUINOJSON_ASSERT(resources_ != nullptr);
562
    VariantImpl(slot.ptr(), resources_).clear();
366✔
563
    resources_->freeVariant(slot);
366✔
564
  }
366✔
565

566
  Slot<VariantData> allocVariant() {
4,854✔
567
    ARDUINOJSON_ASSERT(resources_ != nullptr);
568
    return resources_->allocVariant();
4,854✔
569
  }
570

571
  Slot<VariantData> getPreviousSlot(VariantData*) const;
572

573
  CollectionData* getCollectionData() const {
79,574✔
574
    ARDUINOJSON_ASSERT(data_ != nullptr);
575
    ARDUINOJSON_ASSERT(data_->isCollection());
576
    return &data_->content.asCollection;
79,574✔
577
  }
578
};
579

580
ARDUINOJSON_END_PRIVATE_NAMESPACE
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc