Coverage Report

Created: 2026-03-11 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupd/fwupd-json-object.c
Line
Count
Source
1
/*
2
 * Copyright 2025 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fwupd-error.h"
10
#include "fwupd-json-array-private.h"
11
#include "fwupd-json-common-private.h"
12
#include "fwupd-json-node-private.h"
13
#include "fwupd-json-object-private.h"
14
15
/**
16
 * FwupdJsonObject:
17
 *
18
 * A JSON object.
19
 *
20
 * See also: [struct@FwupdJsonArray] [struct@FwupdJsonNode]
21
 */
22
23
typedef struct {
24
  GRefString *key;
25
  FwupdJsonNode *json_node;
26
} FwupdJsonObjectEntry;
27
28
struct FwupdJsonObject {
29
  grefcount refcount;
30
  GPtrArray *items; /* element-type FwupdJsonObjectEntry */
31
};
32
33
static void
34
fwupd_json_object_entry_free(FwupdJsonObjectEntry *entry)
35
10.1k
{
36
10.1k
  g_ref_string_release(entry->key);
37
10.1k
  fwupd_json_node_unref(entry->json_node);
38
10.1k
  g_free(entry);
39
10.1k
}
40
41
/**
42
 * fwupd_json_object_new: (skip):
43
 *
44
 * Creates a new JSON object.
45
 *
46
 * Returns: (transfer full): a #FwupdJsonObject
47
 *
48
 * Since: 2.1.1
49
 **/
50
FwupdJsonObject *
51
fwupd_json_object_new(void)
52
8.55k
{
53
8.55k
  FwupdJsonObject *self = g_new0(FwupdJsonObject, 1);
54
8.55k
  g_ref_count_init(&self->refcount);
55
8.55k
  self->items = g_ptr_array_new_with_free_func((GDestroyNotify)fwupd_json_object_entry_free);
56
8.55k
  return self;
57
8.55k
}
58
59
/**
60
 * fwupd_json_object_ref: (skip):
61
 * @self: a #FwupdJsonObject
62
 *
63
 * Increases the reference count of a JSON object.
64
 *
65
 * Returns: (transfer full): a #FwupdJsonObject
66
 *
67
 * Since: 2.1.1
68
 **/
69
FwupdJsonObject *
70
fwupd_json_object_ref(FwupdJsonObject *self)
71
11.4k
{
72
11.4k
  g_return_val_if_fail(self != NULL, NULL);
73
11.4k
  g_ref_count_inc(&self->refcount);
74
11.4k
  return self;
75
11.4k
}
76
77
/**
78
 * fwupd_json_object_unref: (skip):
79
 * @self: a #FwupdJsonObject
80
 *
81
 * Decreases the reference count of a JSON object.
82
 *
83
 * Returns: (transfer none): a #FwupdJsonObject, or %NULL
84
 *
85
 * Since: 2.1.1
86
 **/
87
FwupdJsonObject *
88
fwupd_json_object_unref(FwupdJsonObject *self)
89
19.9k
{
90
19.9k
  g_return_val_if_fail(self != NULL, NULL);
91
19.9k
  if (!g_ref_count_dec(&self->refcount))
92
11.4k
    return self;
93
8.55k
  g_ptr_array_unref(self->items);
94
8.55k
  g_free(self);
95
8.55k
  return NULL;
96
19.9k
}
97
98
/**
99
 * fwupd_json_object_clear:
100
 * @self: a #FwupdJsonObject
101
 *
102
 * Clears the member data for the JSON object, but does not affect the refcount.
103
 *
104
 * Since: 2.1.1
105
 **/
106
void
107
fwupd_json_object_clear(FwupdJsonObject *self)
108
0
{
109
0
  g_return_if_fail(self != NULL);
110
0
  g_ptr_array_set_size(self->items, 0);
111
0
}
112
113
/**
114
 * fwupd_json_object_get_size:
115
 * @self: a #FwupdJsonObject
116
 *
117
 * Gets the size of the JSON object.
118
 *
119
 * Returns: number of key-values added
120
 *
121
 * Since: 2.1.1
122
 **/
123
guint
124
fwupd_json_object_get_size(FwupdJsonObject *self)
125
13.5k
{
126
13.5k
  g_return_val_if_fail(self != NULL, G_MAXUINT);
127
13.5k
  return self->items->len;
128
13.5k
}
129
130
/**
131
 * fwupd_json_object_get_key_for_index: (skip):
132
 * @self: a #FwupdJsonObject
133
 * @idx: index
134
 * @error: (nullable): optional return location for an error
135
 *
136
 * Gets the key for a given index position.
137
 *
138
 * Returns: a #GRefString, or %NULL on error
139
 *
140
 * Since: 2.1.1
141
 **/
142
GRefString *
143
fwupd_json_object_get_key_for_index(FwupdJsonObject *self, guint idx, GError **error)
144
0
{
145
0
  FwupdJsonObjectEntry *entry;
146
147
0
  g_return_val_if_fail(self != NULL, NULL);
148
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
149
150
  /* sanity check */
151
0
  if (idx >= self->items->len) {
152
0
    g_set_error(error,
153
0
          FWUPD_ERROR,
154
0
          FWUPD_ERROR_NOT_FOUND,
155
0
          "index %u is larger than object size",
156
0
          idx);
157
0
    return NULL;
158
0
  }
159
160
  /* success */
161
0
  entry = g_ptr_array_index(self->items, idx);
162
0
  return entry->key;
163
0
}
164
165
/**
166
 * fwupd_json_object_get_node_for_index: (skip):
167
 * @self: a #FwupdJsonObject
168
 * @idx: index
169
 * @error: (nullable): optional return location for an error
170
 *
171
 * Gets the node for a given index position.
172
 *
173
 * Returns: (transfer full): a #FwupdJsonNode, or %NULL on error
174
 *
175
 * Since: 2.1.1
176
 **/
177
FwupdJsonNode *
178
fwupd_json_object_get_node_for_index(FwupdJsonObject *self, guint idx, GError **error)
179
0
{
180
0
  FwupdJsonObjectEntry *entry;
181
182
0
  g_return_val_if_fail(self != NULL, NULL);
183
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
184
185
  /* sanity check */
186
0
  if (idx >= self->items->len) {
187
0
    g_set_error(error,
188
0
          FWUPD_ERROR,
189
0
          FWUPD_ERROR_NOT_FOUND,
190
0
          "index %u is larger than object size",
191
0
          idx);
192
0
    return NULL;
193
0
  }
194
195
  /* success */
196
0
  entry = g_ptr_array_index(self->items, idx);
197
0
  return fwupd_json_node_ref(entry->json_node);
198
0
}
199
200
static FwupdJsonObjectEntry *
201
fwupd_json_object_get_entry(FwupdJsonObject *self, const gchar *key, GError **error)
202
13.5k
{
203
33.7k
  for (guint i = 0; i < self->items->len; i++) {
204
23.6k
    FwupdJsonObjectEntry *entry = g_ptr_array_index(self->items, i);
205
23.6k
    if (g_strcmp0(key, entry->key) == 0)
206
3.39k
      return entry;
207
23.6k
  }
208
10.1k
  g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no json_node for key %s", key);
209
10.1k
  return NULL;
210
13.5k
}
211
212
/**
213
 * fwupd_json_object_get_string: (skip):
214
 * @self: a #FwupdJsonObject
215
 * @key: (not nullable): dictionary key
216
 * @error: (nullable): optional return location for an error
217
 *
218
 * Gets a string from a JSON object. An error is returned if @key is not the correct type.
219
 *
220
 * Returns: a #GRefString, or %NULL on error
221
 *
222
 * Since: 2.1.1
223
 **/
224
GRefString *
225
fwupd_json_object_get_string(FwupdJsonObject *self, const gchar *key, GError **error)
226
0
{
227
0
  FwupdJsonObjectEntry *entry;
228
229
0
  g_return_val_if_fail(self != NULL, NULL);
230
0
  g_return_val_if_fail(key != NULL, NULL);
231
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
232
233
0
  entry = fwupd_json_object_get_entry(self, key, error);
234
0
  if (entry == NULL)
235
0
    return NULL;
236
0
  return fwupd_json_node_get_string(entry->json_node, error);
237
0
}
238
239
/**
240
 * fwupd_json_object_get_string_with_default:
241
 * @self: a #FwupdJsonObject
242
 * @key: (not nullable): dictionary key
243
 * @value_default: (not nullable): value to return if @key is not found
244
 * @error: (nullable): optional return location for an error
245
 *
246
 * Gets a string from a JSON object. An error is returned if @key is not the correct type.
247
 *
248
 * Returns: a string, or %NULL on error
249
 *
250
 * Since: 2.1.1
251
 **/
252
const gchar *
253
fwupd_json_object_get_string_with_default(FwupdJsonObject *self,
254
            const gchar *key,
255
            const gchar *value_default,
256
            GError **error)
257
0
{
258
0
  FwupdJsonObjectEntry *entry;
259
260
0
  g_return_val_if_fail(self != NULL, NULL);
261
0
  g_return_val_if_fail(key != NULL, NULL);
262
0
  g_return_val_if_fail(value_default != NULL, NULL);
263
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
264
265
0
  entry = fwupd_json_object_get_entry(self, key, NULL);
266
0
  if (entry == NULL)
267
0
    return value_default;
268
0
  return fwupd_json_node_get_string(entry->json_node, error);
269
0
}
270
271
/**
272
 * fwupd_json_object_get_integer:
273
 * @self: a #FwupdJsonObject
274
 * @key: (not nullable): dictionary key
275
 * @value: (out) (nullable): integer value
276
 * @error: (nullable): optional return location for an error
277
 *
278
 * Gets an integer from a JSON object. An error is returned if @key is not the correct type.
279
 *
280
 * Returns: %TRUE if @value was parsed as an integer
281
 *
282
 * Since: 2.1.1
283
 **/
284
gboolean
285
fwupd_json_object_get_integer(FwupdJsonObject *self,
286
            const gchar *key,
287
            gint64 *value,
288
            GError **error)
289
0
{
290
0
  FwupdJsonObjectEntry *entry;
291
0
  const gchar *str;
292
0
  gchar *endptr = NULL;
293
0
  gint64 value_tmp;
294
295
0
  g_return_val_if_fail(self != NULL, FALSE);
296
0
  g_return_val_if_fail(key != NULL, FALSE);
297
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
298
299
0
  entry = fwupd_json_object_get_entry(self, key, error);
300
0
  if (entry == NULL)
301
0
    return FALSE;
302
0
  str = fwupd_json_node_get_raw(entry->json_node, error);
303
0
  if (str == NULL)
304
0
    return FALSE;
305
306
  /* convert */
307
0
  value_tmp = g_ascii_strtoll(str, &endptr, 10); /* nocheck:blocked */
308
0
  if ((gsize)(endptr - str) != strlen(str)) {
309
0
    g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "cannot parse %s", str);
310
0
    return FALSE;
311
0
  }
312
313
  /* overflow check */
314
0
  if (value_tmp == G_MAXINT64) {
315
0
    g_set_error(error,
316
0
          FWUPD_ERROR,
317
0
          FWUPD_ERROR_INVALID_DATA,
318
0
          "cannot parse %s due to overflow",
319
0
          str);
320
0
    return FALSE;
321
0
  }
322
323
  /* success */
324
0
  if (value != NULL)
325
0
    *value = value_tmp;
326
0
  return TRUE;
327
0
}
328
329
/**
330
 * fwupd_json_object_get_integer_with_default:
331
 * @self: a #FwupdJsonObject
332
 * @key: (not nullable): dictionary key
333
 * @value: (out) (nullable): integer value
334
 * @value_default: value to return if @key is not found, typically 0 or %G_MAXINT64
335
 * @error: (nullable): optional return location for an error
336
 *
337
 * Gets an integer from a JSON object. An error is returned if @key is not the correct type.
338
 *
339
 * Returns: %TRUE if @value was parsed as an integer
340
 *
341
 * Since: 2.1.1
342
 **/
343
gboolean
344
fwupd_json_object_get_integer_with_default(FwupdJsonObject *self,
345
             const gchar *key,
346
             gint64 *value,
347
             gint64 value_default,
348
             GError **error)
349
0
{
350
0
  FwupdJsonObjectEntry *entry;
351
0
  const gchar *str;
352
0
  gchar *endptr = NULL;
353
0
  gint64 value_tmp;
354
355
0
  g_return_val_if_fail(self != NULL, FALSE);
356
0
  g_return_val_if_fail(key != NULL, FALSE);
357
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
358
359
0
  entry = fwupd_json_object_get_entry(self, key, NULL);
360
0
  if (entry == NULL ||
361
0
      fwupd_json_node_get_kind(entry->json_node) == FWUPD_JSON_NODE_KIND_NULL) {
362
0
    if (value != NULL)
363
0
      *value = value_default;
364
0
    return TRUE;
365
0
  }
366
0
  str = fwupd_json_node_get_raw(entry->json_node, error);
367
0
  if (str == NULL)
368
0
    return FALSE;
369
370
  /* convert */
371
0
  value_tmp = g_ascii_strtoll(str, &endptr, 10); /* nocheck:blocked */
372
0
  if ((gsize)(endptr - str) != strlen(str)) {
373
0
    g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "cannot parse %s", str);
374
0
    return FALSE;
375
0
  }
376
377
  /* overflow check */
378
0
  if (value_tmp == G_MAXINT64) {
379
0
    g_set_error(error,
380
0
          FWUPD_ERROR,
381
0
          FWUPD_ERROR_INVALID_DATA,
382
0
          "cannot parse %s due to overflow",
383
0
          str);
384
0
    return FALSE;
385
0
  }
386
387
  /* success */
388
0
  if (value != NULL)
389
0
    *value = value_tmp;
390
0
  return TRUE;
391
0
}
392
393
/**
394
 * fwupd_json_object_get_boolean:
395
 * @self: a #FwupdJsonObject
396
 * @key: (not nullable): dictionary key
397
 * @value: (out) (nullable): boolean value
398
 * @error: (nullable): optional return location for an error
399
 *
400
 * Gets a boolean from a JSON object. An error is returned if @key is not the correct type.
401
 *
402
 * Returns: %TRUE if @value was parsed as an integer
403
 *
404
 * Since: 2.1.1
405
 **/
406
gboolean
407
fwupd_json_object_get_boolean(FwupdJsonObject *self,
408
            const gchar *key,
409
            gboolean *value,
410
            GError **error)
411
0
{
412
0
  FwupdJsonObjectEntry *entry;
413
0
  const gchar *str;
414
415
0
  g_return_val_if_fail(self != NULL, FALSE);
416
0
  g_return_val_if_fail(key != NULL, FALSE);
417
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
418
419
0
  entry = fwupd_json_object_get_entry(self, key, error);
420
0
  if (entry == NULL)
421
0
    return FALSE;
422
0
  str = fwupd_json_node_get_raw(entry->json_node, error);
423
0
  if (str == NULL)
424
0
    return FALSE;
425
426
  /* convert */
427
0
  if (g_ascii_strcasecmp(str, "false") == 0) {
428
0
    if (value != NULL)
429
0
      *value = FALSE;
430
0
    return TRUE;
431
0
  }
432
0
  if (g_ascii_strcasecmp(str, "true") == 0) {
433
0
    if (value != NULL)
434
0
      *value = TRUE;
435
0
    return TRUE;
436
0
  }
437
438
  /* failed */
439
0
  g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "cannot parse %s", str);
440
0
  return FALSE;
441
0
}
442
443
/**
444
 * fwupd_json_object_get_boolean_with_default:
445
 * @self: a #FwupdJsonObject
446
 * @key: (not nullable): dictionary key
447
 * @value: (out) (nullable): boolean value
448
 * @value_default: value to return if @key is not found, typically %FALSE
449
 * @error: (nullable): optional return location for an error
450
 *
451
 * Gets a boolean from a JSON object. An error is returned if @key is not the correct type.
452
 *
453
 * Returns: %TRUE if @value was parsed as an integer
454
 *
455
 * Since: 2.1.1
456
 **/
457
gboolean
458
fwupd_json_object_get_boolean_with_default(FwupdJsonObject *self,
459
             const gchar *key,
460
             gboolean *value,
461
             gboolean value_default,
462
             GError **error)
463
0
{
464
0
  FwupdJsonObjectEntry *entry;
465
0
  const gchar *str;
466
467
0
  g_return_val_if_fail(self != NULL, FALSE);
468
0
  g_return_val_if_fail(key != NULL, FALSE);
469
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
470
471
0
  entry = fwupd_json_object_get_entry(self, key, NULL);
472
0
  if (entry == NULL ||
473
0
      fwupd_json_node_get_kind(entry->json_node) == FWUPD_JSON_NODE_KIND_NULL) {
474
0
    if (value != NULL)
475
0
      *value = value_default;
476
0
    return TRUE;
477
0
  }
478
0
  str = fwupd_json_node_get_raw(entry->json_node, error);
479
0
  if (str == NULL)
480
0
    return FALSE;
481
482
  /* convert */
483
0
  if (g_ascii_strcasecmp(str, "false") == 0) {
484
0
    if (value != NULL)
485
0
      *value = FALSE;
486
0
    return TRUE;
487
0
  }
488
0
  if (g_ascii_strcasecmp(str, "true") == 0) {
489
0
    if (value != NULL)
490
0
      *value = TRUE;
491
0
    return TRUE;
492
0
  }
493
494
  /* failed */
495
0
  g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "cannot parse %s", str);
496
0
  return FALSE;
497
0
}
498
499
/**
500
 * fwupd_json_object_has_node: (skip):
501
 * @self: a #FwupdJsonObject
502
 * @key: (not nullable): dictionary key
503
 *
504
 * Finds if a node exists in a JSON object.
505
 *
506
 * In general, it's nearly always better to call the type-specific method directly e.g.
507
 * fwupd_json_object_get_string() and handle the error.
508
 *
509
 * Returns: %TRUE if a node exists with the key.
510
 *
511
 * Since: 2.1.1
512
 **/
513
gboolean
514
fwupd_json_object_has_node(FwupdJsonObject *self, const gchar *key)
515
0
{
516
0
  g_return_val_if_fail(self != NULL, FALSE);
517
0
  g_return_val_if_fail(key != NULL, FALSE);
518
0
  return fwupd_json_object_get_entry(self, key, NULL) != NULL;
519
0
}
520
521
/**
522
 * fwupd_json_object_get_node: (skip):
523
 * @self: a #FwupdJsonObject
524
 * @key: (not nullable): dictionary key
525
 * @error: (nullable): optional return location for an error
526
 *
527
 * Gets a node from a JSON object.
528
 *
529
 * Returns: (transfer full): a #FwupdJsonObject, or %NULL on error
530
 *
531
 * Since: 2.1.1
532
 **/
533
FwupdJsonNode *
534
fwupd_json_object_get_node(FwupdJsonObject *self, const gchar *key, GError **error)
535
0
{
536
0
  FwupdJsonObjectEntry *entry;
537
538
0
  g_return_val_if_fail(self != NULL, NULL);
539
0
  g_return_val_if_fail(key != NULL, NULL);
540
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
541
542
0
  entry = fwupd_json_object_get_entry(self, key, error);
543
0
  if (entry == NULL)
544
0
    return NULL;
545
0
  return fwupd_json_node_ref(entry->json_node);
546
0
}
547
548
/**
549
 * fwupd_json_object_get_nodes: (skip):
550
 * @self: a #FwupdJsonObject
551
 *
552
 * Gets all the nodes from a JSON object.
553
 *
554
 * Returns: (transfer container) (element-type FwupdJsonNode): an array of nodes
555
 *
556
 * Since: 2.1.1
557
 **/
558
GPtrArray *
559
fwupd_json_object_get_nodes(FwupdJsonObject *self)
560
0
{
561
0
  g_autoptr(GPtrArray) json_nodes =
562
0
      g_ptr_array_new_with_free_func((GDestroyNotify)fwupd_json_node_unref);
563
564
0
  g_return_val_if_fail(self != NULL, NULL);
565
566
0
  for (guint i = 0; i < self->items->len; i++) {
567
0
    FwupdJsonObjectEntry *entry = g_ptr_array_index(self->items, i);
568
0
    g_ptr_array_add(json_nodes, fwupd_json_node_ref(entry->json_node));
569
0
  }
570
0
  return g_steal_pointer(&json_nodes);
571
0
}
572
573
/**
574
 * fwupd_json_object_get_keys: (skip):
575
 * @self: a #FwupdJsonObject
576
 *
577
 * Gets all the keys from a JSON object.
578
 *
579
 * Returns: (transfer container) (element-type GRefString): an array of keys
580
 *
581
 * Since: 2.1.1
582
 **/
583
GPtrArray *
584
fwupd_json_object_get_keys(FwupdJsonObject *self)
585
0
{
586
0
  g_autoptr(GPtrArray) json_keys =
587
0
      g_ptr_array_new_with_free_func((GDestroyNotify)g_ref_string_release);
588
589
0
  g_return_val_if_fail(self != NULL, NULL);
590
591
0
  for (guint i = 0; i < self->items->len; i++) {
592
0
    FwupdJsonObjectEntry *entry = g_ptr_array_index(self->items, i);
593
0
    g_ptr_array_add(json_keys, g_ref_string_acquire(entry->key));
594
0
  }
595
0
  return g_steal_pointer(&json_keys);
596
0
}
597
598
/**
599
 * fwupd_json_object_get_object: (skip):
600
 * @self: a #FwupdJsonObject
601
 * @key: (not nullable): dictionary key
602
 * @error: (nullable): optional return location for an error
603
 *
604
 * Gets a different object from a JSON object. An error is returned if @key is not the correct type.
605
 *
606
 * Returns: (transfer full): a #FwupdJsonObject, or %NULL on error
607
 *
608
 * Since: 2.1.1
609
 **/
610
FwupdJsonObject *
611
fwupd_json_object_get_object(FwupdJsonObject *self, const gchar *key, GError **error)
612
0
{
613
0
  FwupdJsonObjectEntry *entry;
614
615
0
  g_return_val_if_fail(self != NULL, NULL);
616
0
  g_return_val_if_fail(key != NULL, NULL);
617
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
618
619
0
  entry = fwupd_json_object_get_entry(self, key, error);
620
0
  if (entry == NULL)
621
0
    return NULL;
622
0
  return fwupd_json_node_get_object(entry->json_node, error);
623
0
}
624
625
/**
626
 * fwupd_json_object_get_array: (skip):
627
 * @self: a #FwupdJsonObject
628
 * @key: (not nullable): dictionary key
629
 * @error: (nullable): optional return location for an error
630
 *
631
 * Gets an array from a JSON object. An error is returned if @key is not the correct type.
632
 *
633
 * Returns: (transfer full): a #FwupdJsonArray, or %NULL on error
634
 *
635
 * Since: 2.1.1
636
 **/
637
FwupdJsonArray *
638
fwupd_json_object_get_array(FwupdJsonObject *self, const gchar *key, GError **error)
639
0
{
640
0
  FwupdJsonObjectEntry *entry;
641
642
0
  g_return_val_if_fail(self != NULL, NULL);
643
0
  g_return_val_if_fail(key != NULL, NULL);
644
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
645
646
0
  entry = fwupd_json_object_get_entry(self, key, error);
647
0
  if (entry == NULL)
648
0
    return NULL;
649
0
  return fwupd_json_node_get_array(entry->json_node, error);
650
0
}
651
652
void
653
fwupd_json_object_add_raw_internal(FwupdJsonObject *self,
654
           GRefString *key,
655
           GRefString *value,
656
           FwupdJsonLoadFlags flags)
657
2.80k
{
658
2.80k
  FwupdJsonObjectEntry *entry = NULL;
659
660
2.80k
  if ((flags & FWUPD_JSON_LOAD_FLAG_TRUSTED) == 0)
661
2.80k
    entry = fwupd_json_object_get_entry(self, key, NULL);
662
2.80k
  if (entry != NULL) {
663
369
    fwupd_json_node_unref(entry->json_node);
664
2.44k
  } else {
665
2.44k
    entry = g_new0(FwupdJsonObjectEntry, 1);
666
2.44k
    entry->key = (flags & FWUPD_JSON_LOAD_FLAG_STATIC_KEYS) > 0
667
2.44k
         ? g_ref_string_new_intern(key)
668
2.44k
         : g_ref_string_acquire(key);
669
2.44k
    g_ptr_array_add(self->items, entry);
670
2.44k
  }
671
2.80k
  entry->json_node = fwupd_json_node_new_raw_internal(value);
672
2.80k
}
673
674
void
675
fwupd_json_object_add_null_internal(FwupdJsonObject *self,
676
            GRefString *key,
677
            FwupdJsonLoadFlags flags)
678
2.17k
{
679
2.17k
  FwupdJsonObjectEntry *entry = NULL;
680
681
2.17k
  if ((flags & FWUPD_JSON_LOAD_FLAG_TRUSTED) == 0)
682
2.17k
    entry = fwupd_json_object_get_entry(self, key, NULL);
683
2.17k
  if (entry != NULL) {
684
830
    fwupd_json_node_unref(entry->json_node);
685
1.34k
  } else {
686
1.34k
    entry = g_new0(FwupdJsonObjectEntry, 1);
687
1.34k
    entry->key = (flags & FWUPD_JSON_LOAD_FLAG_STATIC_KEYS) > 0
688
1.34k
         ? g_ref_string_new_intern(key)
689
1.34k
         : g_ref_string_acquire(key);
690
1.34k
    g_ptr_array_add(self->items, entry);
691
1.34k
  }
692
2.17k
  entry->json_node = fwupd_json_node_new_null_internal();
693
2.17k
}
694
695
/**
696
 * fwupd_json_object_add_node:
697
 * @self: a #FwupdJsonObject
698
 * @key: (not nullable): dictionary key
699
 * @json_node: (not nullable): a #FwupdJsonNode
700
 *
701
 * Adds a node to the JSON object. If the node already exists the old one is replaced.
702
 *
703
 * Since: 2.1.1
704
 **/
705
void
706
fwupd_json_object_add_node(FwupdJsonObject *self, const gchar *key, FwupdJsonNode *json_node)
707
0
{
708
0
  FwupdJsonObjectEntry *entry;
709
710
0
  g_return_if_fail(self != NULL);
711
0
  g_return_if_fail(key != NULL);
712
0
  g_return_if_fail(json_node != NULL);
713
714
0
  entry = fwupd_json_object_get_entry(self, key, NULL);
715
0
  if (entry != NULL) {
716
0
    fwupd_json_node_unref(entry->json_node);
717
0
  } else {
718
0
    entry = g_new0(FwupdJsonObjectEntry, 1);
719
0
    entry->key = g_ref_string_new(key);
720
0
    g_ptr_array_add(self->items, entry);
721
0
  }
722
0
  entry->json_node = fwupd_json_node_ref(json_node);
723
0
}
724
725
/**
726
 * fwupd_json_object_add_raw:
727
 * @self: a #FwupdJsonObject
728
 * @key: (not nullable): dictionary key
729
 * @value: (not nullable): value
730
 *
731
 * Adds a raw value to the JSON object. If the node already exists the old one is replaced.
732
 *
733
 * Since: 2.1.1
734
 **/
735
void
736
fwupd_json_object_add_raw(FwupdJsonObject *self, const gchar *key, const gchar *value)
737
0
{
738
0
  g_autoptr(FwupdJsonNode) json_node = NULL;
739
740
0
  g_return_if_fail(self != NULL);
741
0
  g_return_if_fail(key != NULL);
742
0
  g_return_if_fail(value != NULL);
743
744
0
  json_node = fwupd_json_node_new_raw(value);
745
0
  fwupd_json_object_add_node(self, key, json_node);
746
0
}
747
748
void
749
fwupd_json_object_add_string_internal(FwupdJsonObject *self,
750
              GRefString *key,
751
              GRefString *value,
752
              FwupdJsonLoadFlags flags)
753
2.34k
{
754
2.34k
  FwupdJsonObjectEntry *entry = NULL;
755
756
2.34k
  if ((flags & FWUPD_JSON_LOAD_FLAG_TRUSTED) == 0)
757
2.34k
    entry = fwupd_json_object_get_entry(self, key, NULL);
758
2.34k
  if (entry != NULL) {
759
317
    fwupd_json_node_unref(entry->json_node);
760
2.03k
  } else {
761
2.03k
    entry = g_new0(FwupdJsonObjectEntry, 1);
762
2.03k
    entry->key = (flags & FWUPD_JSON_LOAD_FLAG_STATIC_KEYS) > 0
763
2.03k
         ? g_ref_string_new_intern(key)
764
2.03k
         : g_ref_string_acquire(key);
765
2.03k
    g_ptr_array_add(self->items, entry);
766
2.03k
  }
767
2.34k
  entry->json_node = fwupd_json_node_new_string_internal(value);
768
2.34k
}
769
770
/**
771
 * fwupd_json_object_add_string:
772
 * @self: a #FwupdJsonObject
773
 * @key: (not nullable): dictionary key
774
 * @value: (nullable): value, or %NULL
775
 *
776
 * Adds a string value to the JSON object. If the node already exists the old one is replaced.
777
 *
778
 * Since: 2.1.1
779
 **/
780
void
781
fwupd_json_object_add_string(FwupdJsonObject *self, const gchar *key, const gchar *value)
782
0
{
783
0
  g_autoptr(FwupdJsonNode) json_node = NULL;
784
785
0
  g_return_if_fail(self != NULL);
786
0
  g_return_if_fail(key != NULL);
787
788
0
  json_node = fwupd_json_node_new_string(value);
789
0
  fwupd_json_object_add_node(self, key, json_node);
790
0
}
791
792
/**
793
 * fwupd_json_object_add_bytes:
794
 * @self: a #FwupdJsonObject
795
 * @key: (not nullable): dictionary key
796
 * @value: (not nullable): value
797
 *
798
 * Adds bytes to the JSON object. They will be base64 encoded as a string.
799
 * If the node already exists the old one is replaced.
800
 *
801
 * Since: 2.1.1
802
 **/
803
void
804
fwupd_json_object_add_bytes(FwupdJsonObject *self, const gchar *key, GBytes *value)
805
0
{
806
0
  g_autoptr(FwupdJsonNode) json_node = NULL;
807
0
  g_autofree gchar *b64data = NULL;
808
0
  const guint8 *buf;
809
0
  gsize bufsz = 0;
810
811
0
  g_return_if_fail(self != NULL);
812
0
  g_return_if_fail(key != NULL);
813
0
  g_return_if_fail(value != NULL);
814
815
0
  buf = g_bytes_get_data(value, &bufsz);
816
0
  b64data = g_base64_encode(buf, bufsz);
817
818
0
  json_node = fwupd_json_node_new_string(b64data);
819
0
  fwupd_json_object_add_node(self, key, json_node);
820
0
}
821
822
/**
823
 * fwupd_json_object_add_array_strv:
824
 * @self: a #FwupdJsonObject
825
 * @key: (not nullable): dictionary key
826
 * @value: (not nullable): value
827
 *
828
 * Adds a string array to the JSON object. If the node already exists the old one is replaced.
829
 *
830
 * Since: 2.1.1
831
 **/
832
void
833
fwupd_json_object_add_array_strv(FwupdJsonObject *self, const gchar *key, gchar **value)
834
0
{
835
0
  g_autoptr(FwupdJsonArray) json_arr = fwupd_json_array_new();
836
837
0
  g_return_if_fail(self != NULL);
838
0
  g_return_if_fail(key != NULL);
839
0
  g_return_if_fail(value != NULL);
840
841
0
  for (guint i = 0; value[i] != NULL; i++)
842
0
    fwupd_json_array_add_string(json_arr, value[i]);
843
0
  fwupd_json_object_add_array(self, key, json_arr);
844
0
}
845
846
/**
847
 * fwupd_json_object_add_integer:
848
 * @self: a #FwupdJsonObject
849
 * @key: (not nullable): dictionary key
850
 * @value: integer
851
 *
852
 * Adds an integer value to the JSON object.
853
 *
854
 * Since: 2.1.1
855
 **/
856
void
857
fwupd_json_object_add_integer(FwupdJsonObject *self, const gchar *key, gint64 value)
858
0
{
859
0
  g_autoptr(FwupdJsonNode) json_node = NULL;
860
0
  g_autofree gchar *str = NULL;
861
862
0
  g_return_if_fail(self != NULL);
863
0
  g_return_if_fail(key != NULL);
864
0
  g_return_if_fail(value != G_MAXINT64);
865
866
0
  str = g_strdup_printf("%" G_GINT64_FORMAT, value);
867
0
  json_node = fwupd_json_node_new_raw(str);
868
0
  fwupd_json_object_add_node(self, key, json_node);
869
0
}
870
871
/**
872
 * fwupd_json_object_add_boolean:
873
 * @self: a #FwupdJsonObject
874
 * @key: (not nullable): dictionary key
875
 * @value: boolean
876
 *
877
 * Adds a boolean value to the JSON object.
878
 *
879
 * Since: 2.1.1
880
 **/
881
void
882
fwupd_json_object_add_boolean(FwupdJsonObject *self, const gchar *key, gboolean value)
883
0
{
884
0
  g_autoptr(FwupdJsonNode) json_node = NULL;
885
886
0
  g_return_if_fail(self != NULL);
887
0
  g_return_if_fail(key != NULL);
888
889
0
  json_node = fwupd_json_node_new_raw(value ? "true" : "false");
890
0
  fwupd_json_object_add_node(self, key, json_node);
891
0
}
892
893
void
894
fwupd_json_object_add_object_internal(FwupdJsonObject *self,
895
              GRefString *key,
896
              FwupdJsonObject *json_obj)
897
3.13k
{
898
3.13k
  FwupdJsonObjectEntry *entry = fwupd_json_object_get_entry(self, key, NULL);
899
3.13k
  if (entry != NULL) {
900
1.39k
    fwupd_json_node_unref(entry->json_node);
901
1.74k
  } else {
902
1.74k
    entry = g_new0(FwupdJsonObjectEntry, 1);
903
1.74k
    entry->key = g_ref_string_acquire(key);
904
1.74k
    g_ptr_array_add(self->items, entry);
905
1.74k
  }
906
3.13k
  entry->json_node = fwupd_json_node_new_object(json_obj);
907
3.13k
}
908
909
/**
910
 * fwupd_json_object_add_object:
911
 * @self: a #FwupdJsonObject
912
 * @key: (not nullable): dictionary key
913
 * @json_obj: a #FwupdJsonObject
914
 *
915
 * Adds a different object to the JSON object.
916
 *
917
 * Since: 2.1.1
918
 **/
919
void
920
fwupd_json_object_add_object(FwupdJsonObject *self, const gchar *key, FwupdJsonObject *json_obj)
921
0
{
922
0
  g_autoptr(FwupdJsonNode) json_node = NULL;
923
924
0
  g_return_if_fail(self != NULL);
925
0
  g_return_if_fail(key != NULL);
926
0
  g_return_if_fail(json_obj != NULL);
927
0
  g_return_if_fail(self != json_obj);
928
929
0
  json_node = fwupd_json_node_new_object(json_obj);
930
0
  fwupd_json_object_add_node(self, key, json_node);
931
0
}
932
933
/**
934
 * fwupd_json_object_add_object_map:
935
 * @self: a #FwupdJsonObject
936
 * @key: (not nullable): dictionary key
937
 * @value: (element-type utf8 utf8): a hash table
938
 *
939
 * Adds a  object to the JSON object.
940
 *
941
 * Since: 2.1.1
942
 **/
943
void
944
fwupd_json_object_add_object_map(FwupdJsonObject *self, const gchar *key, GHashTable *value)
945
0
{
946
0
  GHashTableIter iter;
947
0
  gpointer hash_key, hash_value;
948
0
  g_autoptr(FwupdJsonObject) json_obj = fwupd_json_object_new();
949
950
0
  g_return_if_fail(self != NULL);
951
0
  g_return_if_fail(key != NULL);
952
0
  g_return_if_fail(value != NULL);
953
954
0
  g_hash_table_iter_init(&iter, value);
955
0
  while (g_hash_table_iter_next(&iter, &hash_key, &hash_value)) {
956
0
    fwupd_json_object_add_string(json_obj,
957
0
               (const gchar *)hash_key,
958
0
               (const gchar *)hash_value);
959
0
  }
960
0
  fwupd_json_object_add_object(self, key, json_obj);
961
0
}
962
963
void
964
fwupd_json_object_add_array_internal(FwupdJsonObject *self,
965
             GRefString *key,
966
             FwupdJsonArray *json_arr)
967
3.03k
{
968
3.03k
  FwupdJsonObjectEntry *entry = fwupd_json_object_get_entry(self, key, NULL);
969
3.03k
  if (entry != NULL) {
970
477
    fwupd_json_node_unref(entry->json_node);
971
2.55k
  } else {
972
2.55k
    entry = g_new0(FwupdJsonObjectEntry, 1);
973
2.55k
    entry->key = g_ref_string_acquire(key);
974
2.55k
    g_ptr_array_add(self->items, entry);
975
2.55k
  }
976
3.03k
  entry->json_node = fwupd_json_node_new_array(json_arr);
977
3.03k
}
978
979
/**
980
 * fwupd_json_object_add_array:
981
 * @self: a #FwupdJsonObject
982
 * @key: (not nullable): dictionary key
983
 * @json_arr: a #FwupdJsonArray
984
 *
985
 * Adds an array to the JSON object.
986
 *
987
 * Since: 2.1.1
988
 **/
989
void
990
fwupd_json_object_add_array(FwupdJsonObject *self, const gchar *key, FwupdJsonArray *json_arr)
991
0
{
992
0
  g_autoptr(FwupdJsonNode) json_node = NULL;
993
994
0
  g_return_if_fail(self != NULL);
995
0
  g_return_if_fail(key != NULL);
996
0
  g_return_if_fail(json_arr != NULL);
997
998
0
  json_node = fwupd_json_node_new_array(json_arr);
999
0
  fwupd_json_object_add_node(self, key, json_node);
1000
0
}
1001
1002
/**
1003
 * fwupd_json_object_append_string:
1004
 * @self: a #FwupdJsonObject
1005
 * @str: a #GString
1006
 * @depth: depth, where 0 is the root node
1007
 * @flags: some #FwupdJsonExportFlags e.g. #FWUPD_JSON_EXPORT_FLAG_INDENT
1008
 *
1009
 * Appends the JSON object to existing string.
1010
 *
1011
 * Since: 2.1.1
1012
 **/
1013
void
1014
fwupd_json_object_append_string(FwupdJsonObject *self,
1015
        GString *str,
1016
        guint depth,
1017
        FwupdJsonExportFlags flags)
1018
3.94k
{
1019
3.94k
  g_return_if_fail(self != NULL);
1020
3.94k
  g_return_if_fail(str != NULL);
1021
1022
  /* start */
1023
3.94k
  g_string_append_c(str, '{');
1024
3.94k
  if (flags & FWUPD_JSON_EXPORT_FLAG_INDENT)
1025
0
    g_string_append_c(str, '\n');
1026
1027
8.35k
  for (guint i = 0; i < self->items->len; i++) {
1028
4.40k
    FwupdJsonObjectEntry *entry = g_ptr_array_index(self->items, i);
1029
1030
4.40k
    if (flags & FWUPD_JSON_EXPORT_FLAG_INDENT)
1031
0
      fwupd_json_indent(str, depth + 1);
1032
4.40k
    g_string_append_printf(str, "\"%s\": ", entry->key);
1033
4.40k
    fwupd_json_node_append_string(entry->json_node, str, depth + 1, flags);
1034
4.40k
    if (flags & FWUPD_JSON_EXPORT_FLAG_INDENT) {
1035
0
      if (i != self->items->len - 1)
1036
0
        g_string_append_c(str, ',');
1037
0
      g_string_append_c(str, '\n');
1038
4.40k
    } else {
1039
4.40k
      if (i != self->items->len - 1)
1040
2.84k
        g_string_append(str, ", ");
1041
4.40k
    }
1042
4.40k
  }
1043
1044
  /* end */
1045
3.94k
  if (flags & FWUPD_JSON_EXPORT_FLAG_INDENT)
1046
0
    fwupd_json_indent(str, depth);
1047
3.94k
  g_string_append_c(str, '}');
1048
3.94k
}
1049
1050
/**
1051
 * fwupd_json_object_to_string:
1052
 * @self: a #FwupdJsonObject
1053
 * @flags: some #FwupdJsonExportFlags e.g. #FWUPD_JSON_EXPORT_FLAG_INDENT
1054
 *
1055
 * Converts the JSON object to a string representation.
1056
 *
1057
 * Returns: (transfer full): a #GString
1058
 *
1059
 * Since: 2.1.1
1060
 **/
1061
GString *
1062
fwupd_json_object_to_string(FwupdJsonObject *self, FwupdJsonExportFlags flags)
1063
0
{
1064
0
  GString *str = g_string_new(NULL);
1065
0
  fwupd_json_object_append_string(self, str, 0, flags);
1066
0
  if (flags & FWUPD_JSON_EXPORT_FLAG_TRAILING_NEWLINE)
1067
0
    g_string_append_c(str, '\n');
1068
0
  return str;
1069
0
}
1070
1071
/**
1072
 * fwupd_json_object_to_bytes:
1073
 * @self: a #FwupdJsonObject
1074
 * @flags: some #FwupdJsonExportFlags e.g. #FWUPD_JSON_EXPORT_FLAG_INDENT
1075
 *
1076
 * Converts the JSON object to UTF-8 bytes.
1077
 *
1078
 * Returns: (transfer full): a #GBytes
1079
 *
1080
 * Since: 2.1.1
1081
 **/
1082
GBytes *
1083
fwupd_json_object_to_bytes(FwupdJsonObject *self, FwupdJsonExportFlags flags)
1084
0
{
1085
0
  GString *str = g_string_new(NULL);
1086
0
  fwupd_json_object_append_string(self, str, 0, flags);
1087
0
  if (flags & FWUPD_JSON_EXPORT_FLAG_TRAILING_NEWLINE)
1088
0
    g_string_append_c(str, '\n');
1089
0
  return g_string_free_to_bytes(str);
1090
0
}