Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cpl_json.cpp
Line
Count
Source
1
/******************************************************************************
2
 * Project:  Common Portability Library
3
 * Purpose:  Function wrapper for libjson-c access.
4
 * Author:   Dmitry Baryshnikov, dmitry.baryshnikov@nextgis.com
5
 *
6
 ******************************************************************************
7
 * Copyright (c) 2017-2018 NextGIS, <info@nextgis.com>
8
 *
9
 * SPDX-License-Identifier: MIT
10
 ****************************************************************************/
11
12
#include "cpl_json.h"
13
14
#include "cpl_error.h"
15
#include "cpl_json_header.h"
16
#include "cpl_vsi.h"
17
18
#include "cpl_http.h"
19
#include "cpl_multiproc.h"
20
21
0
#define TO_JSONOBJ(x) static_cast<json_object *>(x)
22
23
static const char *JSON_PATH_DELIMITER = "/";
24
25
static const char *INVALID_OBJ_KEY = "__INVALID_OBJ_KEY__";
26
27
#define JSON_C_VER_014 (14 << 8)
28
29
// json_object_new_uint64() was added in libjson-c 0.14
30
#if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_014)
31
32
static int CPLJSON_json_object_new_uint64_formatter(struct json_object *jso,
33
                                                    struct printbuf *pb,
34
                                                    int /* level */,
35
                                                    int /* flags */)
36
{
37
    const char *pszStr = json_object_get_string(jso);
38
    return printbuf_memappend(pb, pszStr, static_cast<int>(strlen(pszStr)));
39
}
40
41
static json_object *CPLJSON_json_object_new_uint64(uint64_t nVal)
42
{
43
    json_object *jso = json_object_new_string(
44
        CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nVal)));
45
    json_object_set_serializer(jso, CPLJSON_json_object_new_uint64_formatter,
46
                               nullptr, nullptr);
47
    return jso;
48
}
49
50
#define json_object_new_uint64 CPLJSON_json_object_new_uint64
51
52
#endif
53
54
//------------------------------------------------------------------------------
55
// JSONDocument
56
//------------------------------------------------------------------------------
57
/*! @cond Doxygen_Suppress */
58
0
CPLJSONDocument::CPLJSONDocument() : m_poRootJsonObject(nullptr)
59
0
{
60
0
}
61
62
CPLJSONDocument::~CPLJSONDocument()
63
0
{
64
0
    if (m_poRootJsonObject)
65
0
        json_object_put(TO_JSONOBJ(m_poRootJsonObject));
66
0
}
67
68
CPLJSONDocument::CPLJSONDocument(const CPLJSONDocument &other)
69
0
    : m_poRootJsonObject(json_object_get(TO_JSONOBJ(other.m_poRootJsonObject)))
70
0
{
71
0
}
72
73
CPLJSONDocument &CPLJSONDocument::operator=(const CPLJSONDocument &other)
74
0
{
75
0
    if (this == &other)
76
0
        return *this;
77
78
0
    if (m_poRootJsonObject)
79
0
        json_object_put(TO_JSONOBJ(m_poRootJsonObject));
80
0
    m_poRootJsonObject = json_object_get(TO_JSONOBJ(other.m_poRootJsonObject));
81
82
0
    return *this;
83
0
}
84
85
CPLJSONDocument::CPLJSONDocument(CPLJSONDocument &&other)
86
0
    : m_poRootJsonObject(other.m_poRootJsonObject)
87
0
{
88
0
    other.m_poRootJsonObject = nullptr;
89
0
}
90
91
CPLJSONDocument &CPLJSONDocument::operator=(CPLJSONDocument &&other)
92
0
{
93
0
    if (this == &other)
94
0
        return *this;
95
96
0
    if (m_poRootJsonObject)
97
0
        json_object_put(TO_JSONOBJ(m_poRootJsonObject));
98
0
    m_poRootJsonObject = other.m_poRootJsonObject;
99
0
    other.m_poRootJsonObject = nullptr;
100
101
0
    return *this;
102
0
}
103
104
/*! @endcond */
105
106
/**
107
 * Save json document at specified path
108
 * @param  osPath Path to save json document
109
 * @return         true on success. If error occurred it can be received using
110
 * CPLGetLastErrorMsg method.
111
 *
112
 */
113
bool CPLJSONDocument::Save(const std::string &osPath) const
114
0
{
115
0
    VSILFILE *fp = VSIFOpenL(osPath.c_str(), "wt");
116
0
    if (nullptr == fp)
117
0
    {
118
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
119
0
                 "File %s cannot be opened for writing", osPath.c_str());
120
0
        return false;
121
0
    }
122
123
0
    const char *pabyData = json_object_to_json_string_ext(
124
0
        TO_JSONOBJ(m_poRootJsonObject), JSON_C_TO_STRING_PRETTY);
125
0
    bool bRet = VSIFWriteL(pabyData, strlen(pabyData), 1, fp) == 1;
126
127
0
    bRet = VSIFCloseL(fp) == 0 && bRet;
128
129
0
    return bRet;
130
0
}
131
132
/**
133
 * Return the json document as a serialized string.
134
 * @return         serialized document.
135
 *
136
 */
137
std::string CPLJSONDocument::SaveAsString() const
138
0
{
139
0
    return json_object_to_json_string_ext(TO_JSONOBJ(m_poRootJsonObject),
140
0
                                          JSON_C_TO_STRING_PRETTY);
141
0
}
142
143
/**
144
 * Get json document root object
145
 * @return CPLJSONObject class instance
146
 *
147
 * @since GDAL 3.1
148
 */
149
const CPLJSONObject CPLJSONDocument::GetRoot() const
150
0
{
151
0
    return const_cast<CPLJSONDocument *>(this)->GetRoot();
152
0
}
153
154
/**
155
 * Get json document root object
156
 * @return CPLJSONObject class instance
157
 *
158
 */
159
CPLJSONObject CPLJSONDocument::GetRoot()
160
0
{
161
0
    if (nullptr == m_poRootJsonObject)
162
0
    {
163
0
        m_poRootJsonObject = json_object_new_object();
164
0
    }
165
166
0
    if (json_object_get_type(TO_JSONOBJ(m_poRootJsonObject)) == json_type_array)
167
0
    {
168
0
        return CPLJSONArray("", m_poRootJsonObject);
169
0
    }
170
0
    else
171
0
    {
172
0
        return CPLJSONObject("", m_poRootJsonObject);
173
0
    }
174
0
}
175
176
/**
177
 * Set json document root object
178
 * @param oRoot CPLJSONObject root object
179
 *
180
 * @since GDAL 3.4
181
 */
182
void CPLJSONDocument::SetRoot(const CPLJSONObject &oRoot)
183
0
{
184
0
    if (m_poRootJsonObject)
185
0
        json_object_put(TO_JSONOBJ(m_poRootJsonObject));
186
0
    m_poRootJsonObject = json_object_get(TO_JSONOBJ(oRoot.m_poJsonObject));
187
0
}
188
189
/**
190
 * Load json document from file by provided path
191
 * @param  osPath Path to json file.
192
 * @return         true on success. If error occurred it can be received using
193
 * CPLGetLastErrorMsg method.
194
 *
195
 */
196
bool CPLJSONDocument::Load(const std::string &osPath)
197
0
{
198
0
    GByte *pabyOut = nullptr;
199
0
    vsi_l_offset nSize = 0;
200
201
0
    GIntBig nMaxSize = 0;
202
0
    if (CPLParseMemorySize(CPLGetConfigOption("CPL_JSON_MAX_SIZE", "100MB"),
203
0
                           &nMaxSize, nullptr) != CE_None ||
204
0
        nMaxSize <= 0)
205
0
        return false;
206
207
0
    if (!VSIIngestFile(nullptr, osPath.c_str(), &pabyOut, &nSize, nMaxSize))
208
0
    {
209
0
        CPLError(CE_Failure, CPLE_FileIO, "Load json file %s failed",
210
0
                 osPath.c_str());
211
0
        return false;
212
0
    }
213
214
0
    bool bResult = LoadMemory(pabyOut, static_cast<int>(nSize));
215
0
    VSIFree(pabyOut);
216
0
    return bResult;
217
0
}
218
219
/**
220
 * Load json document from memory buffer.
221
 * @param  pabyData Buffer.data.
222
 * @param  nLength  Buffer size.
223
 * @return          true on success. If error occurred it can be received using
224
 * CPLGetLastErrorMsg method.
225
 *
226
 */
227
bool CPLJSONDocument::LoadMemory(const GByte *pabyData, int nLength)
228
0
{
229
0
    if (nullptr == pabyData)
230
0
    {
231
0
        return false;
232
0
    }
233
234
0
    if (m_poRootJsonObject)
235
0
        json_object_put(TO_JSONOBJ(m_poRootJsonObject));
236
237
0
    if (nLength == 4 &&
238
0
        memcmp(reinterpret_cast<const char *>(pabyData), "true", nLength) == 0)
239
0
    {
240
0
        m_poRootJsonObject = json_object_new_boolean(true);
241
0
        return true;
242
0
    }
243
244
0
    if (nLength == 5 &&
245
0
        memcmp(reinterpret_cast<const char *>(pabyData), "false", nLength) == 0)
246
0
    {
247
0
        m_poRootJsonObject = json_object_new_boolean(false);
248
0
        return true;
249
0
    }
250
251
0
    json_tokener *jstok = json_tokener_new();
252
0
    m_poRootJsonObject = json_tokener_parse_ex(
253
0
        jstok, reinterpret_cast<const char *>(pabyData), nLength);
254
0
    bool bParsed = jstok->err == json_tokener_success;
255
0
    if (!bParsed)
256
0
    {
257
0
        CPLError(CE_Failure, CPLE_AppDefined,
258
0
                 "JSON parsing error: %s (at offset %d)",
259
0
                 json_tokener_error_desc(jstok->err), jstok->char_offset);
260
0
        json_tokener_free(jstok);
261
0
        return false;
262
0
    }
263
0
    json_tokener_free(jstok);
264
0
    return bParsed;
265
0
}
266
267
/**
268
 * Load json document from memory buffer.
269
 * @param  osStr    String
270
 * @return          true on success. If error occurred it can be received using
271
 * CPLGetLastErrorMsg method.
272
 *
273
 */
274
bool CPLJSONDocument::LoadMemory(const std::string &osStr)
275
0
{
276
0
    if (osStr.empty())
277
0
        return false;
278
0
    return LoadMemory(reinterpret_cast<const GByte *>(osStr.data()),
279
0
                      static_cast<int>(osStr.size()));
280
0
}
281
282
/**
283
 * Load json document from file using small chunks of data.
284
 * @param  osPath      Path to json document file.
285
 * @param  nChunkSize   Chunk size.
286
 * @param  pfnProgress  a function to report progress of the json data loading.
287
 * @param  pProgressArg application data passed into progress function.
288
 * @return              true on success. If error occurred it can be received
289
 * using CPLGetLastErrorMsg method.
290
 *
291
 */
292
bool CPLJSONDocument::LoadChunks(const std::string &osPath, size_t nChunkSize,
293
                                 GDALProgressFunc pfnProgress,
294
                                 void *pProgressArg)
295
0
{
296
0
    VSIStatBufL sStatBuf;
297
0
    if (VSIStatL(osPath.c_str(), &sStatBuf) != 0)
298
0
    {
299
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", osPath.c_str());
300
0
        return false;
301
0
    }
302
303
0
    VSILFILE *fp = VSIFOpenL(osPath.c_str(), "rb");
304
0
    if (fp == nullptr)
305
0
    {
306
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", osPath.c_str());
307
0
        return false;
308
0
    }
309
310
0
    void *pBuffer = CPLMalloc(nChunkSize);
311
0
    json_tokener *tok = json_tokener_new();
312
0
    bool bSuccess = true;
313
0
    GUInt32 nFileSize = static_cast<GUInt32>(sStatBuf.st_size);
314
0
    double dfTotalRead = 0.0;
315
316
0
    while (true)
317
0
    {
318
0
        size_t nRead = VSIFReadL(pBuffer, 1, nChunkSize, fp);
319
0
        dfTotalRead += nRead;
320
321
0
        if (m_poRootJsonObject)
322
0
            json_object_put(TO_JSONOBJ(m_poRootJsonObject));
323
324
0
        m_poRootJsonObject = json_tokener_parse_ex(
325
0
            tok, static_cast<const char *>(pBuffer), static_cast<int>(nRead));
326
327
0
        enum json_tokener_error jerr = json_tokener_get_error(tok);
328
0
        if (jerr != json_tokener_continue && jerr != json_tokener_success)
329
0
        {
330
0
            CPLError(CE_Failure, CPLE_AppDefined, "JSON error: %s",
331
0
                     json_tokener_error_desc(jerr));
332
0
            bSuccess = false;
333
0
            break;
334
0
        }
335
336
0
        if (nRead < nChunkSize)
337
0
        {
338
0
            break;
339
0
        }
340
341
0
        if (nullptr != pfnProgress)
342
0
        {
343
0
            pfnProgress(dfTotalRead / nFileSize, "Loading ...", pProgressArg);
344
0
        }
345
0
    }
346
347
0
    json_tokener_free(tok);
348
0
    CPLFree(pBuffer);
349
0
    VSIFCloseL(fp);
350
351
0
    if (nullptr != pfnProgress)
352
0
    {
353
0
        pfnProgress(1.0, "Loading ...", pProgressArg);
354
0
    }
355
356
0
    return bSuccess;
357
0
}
358
359
/*! @cond Doxygen_Suppress */
360
#ifdef HAVE_CURL
361
362
typedef struct
363
{
364
    json_object *pObject;
365
    json_tokener *pTokener;
366
} JsonContext, *JsonContextL;
367
368
static size_t CPLJSONWriteFunction(void *pBuffer, size_t nSize, size_t nMemb,
369
                                   void *pUserData)
370
{
371
    size_t nLength = nSize * nMemb;
372
    JsonContextL ctx = static_cast<JsonContextL>(pUserData);
373
    if (ctx->pObject != nullptr)
374
    {
375
        CPLError(CE_Failure, CPLE_AppDefined,
376
                 "A complete JSon object had already been parsed before new "
377
                 "content is appended to it");
378
        return 0;
379
    }
380
    ctx->pObject =
381
        json_tokener_parse_ex(ctx->pTokener, static_cast<const char *>(pBuffer),
382
                              static_cast<int>(nLength));
383
    switch (json_tokener_get_error(ctx->pTokener))
384
    {
385
        case json_tokener_continue:
386
        case json_tokener_success:
387
            return nLength;
388
        default:
389
            return 0; /* error: interrupt the transfer */
390
    }
391
}
392
393
#endif  // HAVE_CURL
394
/*! @endcond */
395
396
/**
397
 * Load json document from web.
398
 * @param  osUrl       Url to json document.
399
 * @param  papszOptions Option list as a NULL-terminated array of strings. May
400
 * be NULL. The available keys are same for CPLHTTPFetch method. Additional key
401
 * JSON_DEPTH define json parse depth. Default is 10.
402
 * @param  pfnProgress  a function to report progress of the json data loading.
403
 * @param  pProgressArg application data passed into progress function.
404
 * @return              true on success. If error occurred it can be received
405
 * using CPLGetLastErrorMsg method.
406
 *
407
 */
408
409
#ifdef HAVE_CURL
410
bool CPLJSONDocument::LoadUrl(const std::string &osUrl,
411
                              const char *const *papszOptions,
412
                              GDALProgressFunc pfnProgress, void *pProgressArg)
413
#else
414
bool CPLJSONDocument::LoadUrl(const std::string & /*osUrl*/,
415
                              const char *const * /*papszOptions*/,
416
                              GDALProgressFunc /*pfnProgress*/,
417
                              void * /*pProgressArg*/)
418
#endif  // HAVE_CURL
419
0
{
420
#ifdef HAVE_CURL
421
    int nDepth =
422
        atoi(CSLFetchNameValueDef(papszOptions, "JSON_DEPTH",
423
                                  "32"));  // Same as JSON_TOKENER_DEFAULT_DEPTH
424
    JsonContext ctx = {nullptr, json_tokener_new_ex(nDepth)};
425
426
    CPLHTTPFetchWriteFunc pWriteFunc = CPLJSONWriteFunction;
427
    CPLHTTPResult *psResult =
428
        CPLHTTPFetchEx(osUrl.c_str(), papszOptions, pfnProgress, pProgressArg,
429
                       pWriteFunc, &ctx);
430
431
    bool bResult =
432
        psResult->nStatus == 0 /*CURLE_OK*/ && psResult->pszErrBuf == nullptr;
433
434
    CPLHTTPDestroyResult(psResult);
435
436
    enum json_tokener_error jerr;
437
    if ((jerr = json_tokener_get_error(ctx.pTokener)) != json_tokener_success)
438
    {
439
        CPLError(CE_Failure, CPLE_AppDefined, "JSON error: %s\n",
440
                 json_tokener_error_desc(jerr));
441
        bResult = false;
442
    }
443
    else
444
    {
445
        if (m_poRootJsonObject)
446
            json_object_put(TO_JSONOBJ(m_poRootJsonObject));
447
448
        m_poRootJsonObject = ctx.pObject;
449
    }
450
    json_tokener_free(ctx.pTokener);
451
452
    return bResult;
453
#else
454
0
    CPLError(CE_Failure, CPLE_NotSupported,
455
0
             "LoadUrl() not supported in a build without Curl");
456
0
    return false;
457
0
#endif
458
0
}
459
460
//------------------------------------------------------------------------------
461
// JSONObject
462
//------------------------------------------------------------------------------
463
/*! @cond Doxygen_Suppress */
464
0
CPLJSONObject::CPLJSONObject() : m_poJsonObject(json_object_new_object())
465
0
{
466
0
}
467
468
0
CPLJSONObject::CPLJSONObject(std::nullptr_t) : m_poJsonObject(nullptr)
469
0
{
470
0
}
471
472
CPLJSONObject::CPLJSONObject(const std::string &osVal)
473
0
    : m_poJsonObject(json_object_new_string(osVal.c_str()))
474
0
{
475
0
}
476
477
CPLJSONObject::CPLJSONObject(const char *pszValue)
478
0
    : m_poJsonObject(json_object_new_string(pszValue))
479
0
{
480
0
}
481
482
CPLJSONObject::CPLJSONObject(bool bVal)
483
0
    : m_poJsonObject(json_object_new_boolean(bVal))
484
0
{
485
0
}
486
487
CPLJSONObject::CPLJSONObject(int nVal)
488
0
    : m_poJsonObject(json_object_new_int(nVal))
489
0
{
490
0
}
491
492
CPLJSONObject::CPLJSONObject(int64_t nVal)
493
0
    : m_poJsonObject(json_object_new_int64(nVal))
494
0
{
495
0
}
496
497
CPLJSONObject::CPLJSONObject(uint64_t nVal)
498
0
    : m_poJsonObject(json_object_new_uint64(nVal))
499
0
{
500
0
}
501
502
CPLJSONObject::CPLJSONObject(double dfVal)
503
0
    : m_poJsonObject(json_object_new_double(dfVal))
504
0
{
505
0
}
506
507
CPLJSONObject::CPLJSONObject(const std::string &osName,
508
                             const CPLJSONObject &oParent)
509
0
    : m_poJsonObject(json_object_get(json_object_new_object())), m_osKey(osName)
510
0
{
511
0
    json_object_object_add(TO_JSONOBJ(oParent.m_poJsonObject), osName.c_str(),
512
0
                           TO_JSONOBJ(m_poJsonObject));
513
0
}
514
515
CPLJSONObject::CPLJSONObject(const std::string &osName,
516
                             JSONObjectH poJsonObject)
517
0
    : m_poJsonObject(json_object_get(TO_JSONOBJ(poJsonObject))), m_osKey(osName)
518
0
{
519
0
}
520
521
CPLJSONObject CPLJSONObject::Clone() const
522
0
{
523
0
    CPLJSONObject oRet;
524
0
    if (IsValid())
525
0
    {
526
0
        CPLJSONDocument oTmpDoc;
527
0
        oTmpDoc.SetRoot(*this);
528
0
        std::string osStr = oTmpDoc.SaveAsString();
529
0
        CPL_IGNORE_RET_VAL(oTmpDoc.LoadMemory(osStr));
530
0
        oRet = oTmpDoc.GetRoot();
531
0
    }
532
0
    return oRet;
533
0
}
534
535
CPLJSONObject::~CPLJSONObject()
536
0
{
537
    // Should delete m_poJsonObject only if CPLJSONObject has no parent
538
0
    if (m_poJsonObject)
539
0
    {
540
0
        json_object_put(TO_JSONOBJ(m_poJsonObject));
541
0
        m_poJsonObject = nullptr;
542
0
    }
543
0
}
544
545
CPLJSONObject::CPLJSONObject(const CPLJSONObject &other)
546
0
    : m_poJsonObject(json_object_get(TO_JSONOBJ(other.m_poJsonObject))),
547
0
      m_osKey(other.m_osKey), m_osKeyForSet(other.m_osKeyForSet)
548
0
{
549
0
}
550
551
CPLJSONObject::CPLJSONObject(CPLJSONObject &&other)
552
0
    : m_poJsonObject(other.m_poJsonObject), m_osKey(std::move(other.m_osKey)),
553
0
      m_osKeyForSet(std::move(other.m_osKeyForSet))
554
0
{
555
0
    other.m_poJsonObject = nullptr;
556
0
}
557
558
CPLJSONObject &CPLJSONObject::operator=(const CPLJSONObject &other)
559
0
{
560
0
    if (this == &other)
561
0
        return *this;
562
563
0
    if (!m_osKeyForSet.empty())
564
0
    {
565
0
        std::string osKeyForSet = m_osKeyForSet;
566
0
        m_osKeyForSet.clear();
567
0
        Set(osKeyForSet, other);
568
0
    }
569
0
    else
570
0
    {
571
0
        m_osKey = other.m_osKey;
572
0
        if (m_poJsonObject)
573
0
            json_object_put(TO_JSONOBJ(m_poJsonObject));
574
0
        m_poJsonObject = json_object_get(TO_JSONOBJ(other.m_poJsonObject));
575
0
    }
576
0
    return *this;
577
0
}
578
579
CPLJSONObject &CPLJSONObject::operator=(CPLJSONObject &&other)
580
0
{
581
0
    if (this == &other)
582
0
        return *this;
583
584
0
    if (!m_osKeyForSet.empty())
585
0
    {
586
0
        if (other.m_poJsonObject)
587
0
        {
588
0
            json_object_object_add(TO_JSONOBJ(GetInternalHandle()),
589
0
                                   m_osKeyForSet.c_str(),
590
0
                                   TO_JSONOBJ(other.m_poJsonObject));
591
0
            other.m_poJsonObject = nullptr;
592
0
        }
593
0
        other.m_osKey = INVALID_OBJ_KEY;
594
0
        m_osKeyForSet.clear();
595
0
    }
596
0
    else
597
0
    {
598
0
        m_osKey = std::move(other.m_osKey);
599
0
        if (m_poJsonObject)
600
0
            json_object_put(TO_JSONOBJ(m_poJsonObject));
601
0
        m_poJsonObject = other.m_poJsonObject;
602
0
        other.m_poJsonObject = nullptr;
603
0
    }
604
0
    return *this;
605
0
}
606
607
CPLJSONObject &CPLJSONObject::operator=(CPLJSONArray &&other)
608
0
{
609
0
    return operator=(static_cast<CPLJSONObject &&>(other));
610
0
}
611
612
/*! @endcond */
613
614
/**
615
 * Add new key - value pair to json object.
616
 * @param osName Key name.
617
 * @param osValue String value.
618
 *
619
 */
620
void CPLJSONObject::Add(const std::string &osName, const std::string &osValue)
621
0
{
622
0
    std::string objectName;
623
0
    if (m_osKey == INVALID_OBJ_KEY)
624
0
        m_osKey.clear();
625
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
626
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
627
0
                                object.m_poJsonObject)) == json_type_object)
628
0
    {
629
0
        json_object *poVal = json_object_new_string(osValue.c_str());
630
0
        json_object_object_add(TO_JSONOBJ(object.GetInternalHandle()),
631
0
                               objectName.c_str(), poVal);
632
0
    }
633
0
}
634
635
/** Add new key - value pair to json object.
636
 *
637
 * @param osName Key name.
638
 * @param svValue String value.
639
 * @since 3.13
640
 */
641
void CPLJSONObject::Add(const std::string &osName, std::string_view svValue)
642
0
{
643
0
    std::string objectName;
644
0
    if (m_osKey == INVALID_OBJ_KEY)
645
0
        m_osKey.clear();
646
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
647
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
648
0
                                object.m_poJsonObject)) == json_type_object)
649
0
    {
650
0
        if (svValue.size() > static_cast<size_t>(INT_MAX - 1))
651
0
        {
652
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too long string view");
653
0
            return;
654
0
        }
655
0
        json_object *poVal = json_object_new_string_len(
656
0
            svValue.data(), static_cast<int>(svValue.size()));
657
0
        json_object_object_add(TO_JSONOBJ(object.GetInternalHandle()),
658
0
                               objectName.c_str(), poVal);
659
0
    }
660
0
}
661
662
/**
663
 * Add new key - value pair to json object.
664
 * @param osName Key name.
665
 * @param pszValue String value.
666
 *
667
 */
668
void CPLJSONObject::Add(const std::string &osName, const char *pszValue)
669
0
{
670
0
    if (nullptr == pszValue)
671
0
    {
672
0
        return;
673
0
    }
674
0
    if (m_osKey == INVALID_OBJ_KEY)
675
0
        m_osKey.clear();
676
0
    std::string objectName;
677
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
678
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
679
0
                                object.m_poJsonObject)) == json_type_object)
680
0
    {
681
0
        json_object *poVal = json_object_new_string(pszValue);
682
0
        json_object_object_add(TO_JSONOBJ(object.GetInternalHandle()),
683
0
                               objectName.c_str(), poVal);
684
0
    }
685
0
}
686
687
// defined in ogr/ogrsf_frmts/geojson/ogrgeojsonwriter.cpp
688
CPL_C_START
689
/* %.XXXg formatting */
690
json_object CPL_DLL *
691
json_object_new_double_with_significant_figures(double dfVal,
692
                                                int nSignificantFigures);
693
CPL_C_END
694
695
/**
696
 * Add new key - value pair to json object.
697
 * @param osName  Key name.
698
 * @param dfValue Double value.
699
 *
700
 */
701
void CPLJSONObject::Add(const std::string &osName, double dfValue)
702
0
{
703
0
    std::string objectName;
704
0
    if (m_osKey == INVALID_OBJ_KEY)
705
0
        m_osKey.clear();
706
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
707
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
708
0
                                object.m_poJsonObject)) == json_type_object)
709
0
    {
710
0
        json_object *poVal =
711
0
            json_object_new_double_with_significant_figures(dfValue, -1);
712
0
        json_object_object_add(TO_JSONOBJ(object.GetInternalHandle()),
713
0
                               objectName.c_str(), poVal);
714
0
    }
715
0
}
716
717
/**
718
 * Add new key - value pair to json object.
719
 * @param osName  Key name.
720
 * @param nValue Integer value.
721
 *
722
 */
723
void CPLJSONObject::Add(const std::string &osName, int nValue)
724
0
{
725
0
    std::string objectName;
726
0
    if (m_osKey == INVALID_OBJ_KEY)
727
0
        m_osKey.clear();
728
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
729
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
730
0
                                object.m_poJsonObject)) == json_type_object)
731
0
    {
732
0
        json_object *poVal = json_object_new_int(nValue);
733
0
        json_object_object_add(TO_JSONOBJ(object.GetInternalHandle()),
734
0
                               objectName.c_str(), poVal);
735
0
    }
736
0
}
737
738
/**
739
 * Add new key - value pair to json object.
740
 * @param osName  Key name.
741
 * @param nValue Long value.
742
 *
743
 */
744
void CPLJSONObject::Add(const std::string &osName, GInt64 nValue)
745
0
{
746
0
    std::string objectName;
747
0
    if (m_osKey == INVALID_OBJ_KEY)
748
0
        m_osKey.clear();
749
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
750
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
751
0
                                object.m_poJsonObject)) == json_type_object)
752
0
    {
753
0
        json_object *poVal =
754
0
            json_object_new_int64(static_cast<int64_t>(nValue));
755
0
        json_object_object_add(TO_JSONOBJ(object.GetInternalHandle()),
756
0
                               objectName.c_str(), poVal);
757
0
    }
758
0
}
759
760
/**
761
 * Add new key - value pair to json object.
762
 * @param osName  Key name.
763
 * @param nValue uint64_t value.
764
 *
765
 * @since GDAL 3.8
766
 */
767
void CPLJSONObject::Add(const std::string &osName, uint64_t nValue)
768
0
{
769
0
    std::string objectName;
770
0
    if (m_osKey == INVALID_OBJ_KEY)
771
0
        m_osKey.clear();
772
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
773
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
774
0
                                object.m_poJsonObject)) == json_type_object)
775
0
    {
776
0
        json_object *poVal = json_object_new_uint64(nValue);
777
0
        json_object_object_add(TO_JSONOBJ(object.GetInternalHandle()),
778
0
                               objectName.c_str(), poVal);
779
0
    }
780
0
}
781
782
/**
783
 * Add new key - value pair to json object.
784
 * @param osName  Key name.
785
 * @param oValue   Array value.
786
 *
787
 */
788
void CPLJSONObject::Add(const std::string &osName, const CPLJSONArray &oValue)
789
0
{
790
0
    std::string objectName;
791
0
    if (m_osKey == INVALID_OBJ_KEY)
792
0
        m_osKey.clear();
793
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
794
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
795
0
                                object.m_poJsonObject)) == json_type_object)
796
0
    {
797
0
        json_object_object_add(
798
0
            TO_JSONOBJ(object.GetInternalHandle()), objectName.c_str(),
799
0
            json_object_get(TO_JSONOBJ(oValue.GetInternalHandle())));
800
0
    }
801
0
}
802
803
/**
804
 * Add new key - value pair to json object.
805
 * @param osName  Key name.
806
 * @param oValue   Json object value.
807
 *
808
 */
809
void CPLJSONObject::Add(const std::string &osName, const CPLJSONObject &oValue)
810
0
{
811
0
    std::string objectName;
812
0
    if (m_osKey == INVALID_OBJ_KEY)
813
0
        m_osKey.clear();
814
0
    if (osName.empty())
815
0
    {
816
0
        json_object_object_add(
817
0
            TO_JSONOBJ(GetInternalHandle()), "",
818
0
            json_object_get(TO_JSONOBJ(oValue.GetInternalHandle())));
819
0
        return;
820
0
    }
821
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
822
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
823
0
                                object.m_poJsonObject)) == json_type_object)
824
0
    {
825
0
        json_object_object_add(
826
0
            TO_JSONOBJ(object.GetInternalHandle()), objectName.c_str(),
827
0
            json_object_get(TO_JSONOBJ(oValue.GetInternalHandle())));
828
0
    }
829
0
}
830
831
/**
832
 * Add new key - value pair to json object.
833
 * @param osName  Key name (do not split it on '/')
834
 * @param oValue   Json object value.
835
 *
836
 * @since GDAL 3.2
837
 */
838
void CPLJSONObject::AddNoSplitName(const std::string &osName,
839
                                   const CPLJSONObject &oValue)
840
0
{
841
0
    if (m_osKey == INVALID_OBJ_KEY)
842
0
        m_osKey.clear();
843
0
    if (IsValid() &&
844
0
        json_object_get_type(TO_JSONOBJ(m_poJsonObject)) == json_type_object)
845
0
    {
846
0
        json_object_object_add(
847
0
            TO_JSONOBJ(GetInternalHandle()), osName.c_str(),
848
0
            json_object_get(TO_JSONOBJ(oValue.GetInternalHandle())));
849
0
    }
850
0
}
851
852
/**
853
 * Add new key - value pair to json object.
854
 * @param osName  Key name.
855
 * @param bValue   Boolean value.
856
 *
857
 */
858
void CPLJSONObject::Add(const std::string &osName, bool bValue)
859
0
{
860
0
    std::string objectName;
861
0
    if (m_osKey == INVALID_OBJ_KEY)
862
0
        m_osKey.clear();
863
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
864
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
865
0
                                object.m_poJsonObject)) == json_type_object)
866
0
    {
867
0
        json_object *poVal = json_object_new_boolean(bValue);
868
0
        json_object_object_add(TO_JSONOBJ(object.GetInternalHandle()),
869
0
                               objectName.c_str(), poVal);
870
0
    }
871
0
}
872
873
/**
874
 * Add new key - null pair to json object.
875
 * @param osName  Key name.
876
 *
877
 */
878
void CPLJSONObject::AddNull(const std::string &osName)
879
0
{
880
0
    std::string objectName;
881
0
    if (m_osKey == INVALID_OBJ_KEY)
882
0
        m_osKey.clear();
883
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
884
0
    if (object.IsValid() && json_object_get_type(TO_JSONOBJ(
885
0
                                object.m_poJsonObject)) == json_type_object)
886
0
    {
887
0
        json_object_object_add(TO_JSONOBJ(object.GetInternalHandle()),
888
0
                               objectName.c_str(), nullptr);
889
0
    }
890
0
}
891
892
/**
893
 * Change value by key.
894
 * @param osName  Key name.
895
 *
896
 */
897
void CPLJSONObject::SetNull(const std::string &osName)
898
0
{
899
0
    Delete(osName);
900
0
    AddNull(osName);
901
0
}
902
903
/**
904
 * Get value by key.
905
 * @param  osName Key name.
906
 * @return         Json array object.
907
 *
908
 */
909
CPLJSONArray CPLJSONObject::GetArray(const std::string &osName) const
910
0
{
911
0
    std::string objectName;
912
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
913
0
    if (object.IsValid())
914
0
    {
915
0
        json_object *poVal = nullptr;
916
0
        if (json_object_object_get_ex(TO_JSONOBJ(object.GetInternalHandle()),
917
0
                                      objectName.c_str(), &poVal))
918
0
        {
919
0
            if (poVal && json_object_get_type(poVal) == json_type_array)
920
0
            {
921
0
                return CPLJSONArray(objectName, poVal);
922
0
            }
923
0
        }
924
0
    }
925
0
    return CPLJSONArray(INVALID_OBJ_KEY, nullptr);
926
0
}
927
928
/**
929
 * Get value by key.
930
 * @param  osName Key name.
931
 * @return         Json object.
932
 *
933
 */
934
CPLJSONObject CPLJSONObject::GetObj(const std::string &osName) const
935
0
{
936
0
    std::string objectName;
937
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
938
0
    if (object.IsValid())
939
0
    {
940
0
        json_object *poVal = nullptr;
941
0
        if (json_object_object_get_ex(TO_JSONOBJ(object.GetInternalHandle()),
942
0
                                      objectName.c_str(), &poVal))
943
0
        {
944
0
            return CPLJSONObject(objectName, poVal);
945
0
        }
946
0
    }
947
0
    return CPLJSONObject(INVALID_OBJ_KEY, nullptr);
948
0
}
949
950
/**
951
 * Get value by key (without splitting on /).
952
 * @param  osName Key name.
953
 * @return         Json object.
954
 * @since 3.13
955
 *
956
 */
957
CPLJSONObject CPLJSONObject::GetObjNoSplitName(const std::string &osName) const
958
0
{
959
0
    json_object *poVal = nullptr;
960
961
    // Typically for keys that contain / character
962
0
    if (json_object_object_get_ex(TO_JSONOBJ(GetInternalHandle()),
963
0
                                  osName.c_str(), &poVal))
964
0
    {
965
0
        return CPLJSONObject(osName, poVal);
966
0
    }
967
0
    return CPLJSONObject(INVALID_OBJ_KEY, nullptr);
968
0
}
969
970
/**
971
 * Get value by key.
972
 * @param  osName Key name.
973
 * @return         Json object.
974
 *
975
 */
976
CPLJSONObject CPLJSONObject::operator[](const std::string &osName) const
977
0
{
978
0
    return GetObj(osName);
979
0
}
980
981
/** Change value by key.
982
 *
983
 * e.g.: ``oObj["type"] = "MyType"``
984
 *
985
 * @since 3.12
986
 */
987
CPLJSONObject CPLJSONObject::operator[](const std::string &osName)
988
0
{
989
0
    auto oObj = GetObj(osName);
990
0
    if (oObj.IsValid())
991
0
        return oObj;
992
0
    CPLJSONObject oClone(*this);
993
0
    oClone.m_osKey = INVALID_OBJ_KEY;
994
0
    oClone.m_osKeyForSet = osName;
995
0
    return oClone;
996
0
}
997
998
/**
999
 * Delete json object by key.
1000
 * @param  osName Key name.
1001
 *
1002
 */
1003
void CPLJSONObject::Delete(const std::string &osName)
1004
0
{
1005
0
    std::string objectName;
1006
0
    if (m_osKey == INVALID_OBJ_KEY)
1007
0
        m_osKey.clear();
1008
0
    CPLJSONObject object = GetObjectByPath(osName, objectName);
1009
0
    if (object.IsValid())
1010
0
    {
1011
0
        json_object_object_del(TO_JSONOBJ(object.GetInternalHandle()),
1012
0
                               objectName.c_str());
1013
0
    }
1014
0
}
1015
1016
/**
1017
 * Delete json object by key (without splitting on /)
1018
 * @param  osName Key name.
1019
 *
1020
 * @since GDAL 3.4
1021
 */
1022
void CPLJSONObject::DeleteNoSplitName(const std::string &osName)
1023
0
{
1024
0
    if (m_osKey == INVALID_OBJ_KEY)
1025
0
        m_osKey.clear();
1026
0
    if (m_poJsonObject)
1027
0
    {
1028
0
        json_object_object_del(TO_JSONOBJ(m_poJsonObject), osName.c_str());
1029
0
    }
1030
0
}
1031
1032
/**
1033
 * Get value by key.
1034
 * @param  osName    Key name.
1035
 * @param  osDefault Default value.
1036
 * @return            String value.
1037
 *
1038
 */
1039
std::string CPLJSONObject::GetString(const std::string &osName,
1040
                                     const std::string &osDefault) const
1041
0
{
1042
0
    if (!m_osKeyForSet.empty())
1043
0
        return osDefault;
1044
0
    CPLJSONObject object = GetObj(osName);
1045
0
    return object.ToString(osDefault);
1046
0
}
1047
1048
/**
1049
 * Get value.
1050
 * @param  osDefault Default value.
1051
 * @return            String value.
1052
 *
1053
 */
1054
std::string CPLJSONObject::ToString(const std::string &osDefault) const
1055
0
{
1056
0
    if (!m_osKeyForSet.empty())
1057
0
        return osDefault;
1058
0
    if( m_poJsonObject /*&& json_object_get_type( TO_JSONOBJ(m_poJsonObject) ) ==
1059
0
            json_type_string*/ )
1060
0
    {
1061
0
        const char *pszString =
1062
0
            json_object_get_string(TO_JSONOBJ(m_poJsonObject));
1063
0
        if (nullptr != pszString)
1064
0
        {
1065
0
            return pszString;
1066
0
        }
1067
0
    }
1068
0
    return osDefault;
1069
0
}
1070
1071
/**
1072
 * Get value by key.
1073
 * @param  osName    Key name.
1074
 * @param  dfDefault  Default value.
1075
 * @return            Double value.
1076
 *
1077
 */
1078
double CPLJSONObject::GetDouble(const std::string &osName,
1079
                                double dfDefault) const
1080
0
{
1081
0
    if (!m_osKeyForSet.empty())
1082
0
        return dfDefault;
1083
0
    CPLJSONObject object = GetObj(osName);
1084
0
    return object.ToDouble(dfDefault);
1085
0
}
1086
1087
/**
1088
 * Get value
1089
 * @param  dfDefault  Default value.
1090
 * @return            Double value.
1091
 *
1092
 */
1093
double CPLJSONObject::ToDouble(double dfDefault) const
1094
0
{
1095
0
    if (!m_osKeyForSet.empty())
1096
0
        return dfDefault;
1097
0
    if( m_poJsonObject /*&& json_object_get_type( TO_JSONOBJ(m_poJsonObject) ) ==
1098
0
            json_type_double*/ )
1099
0
        return json_object_get_double(TO_JSONOBJ(m_poJsonObject));
1100
0
    return dfDefault;
1101
0
}
1102
1103
/**
1104
 * Get value by key.
1105
 * @param  osName    Key name.
1106
 * @param  nDefault   Default value.
1107
 * @return            Integer value.
1108
 *
1109
 */
1110
int CPLJSONObject::GetInteger(const std::string &osName, int nDefault) const
1111
0
{
1112
0
    if (!m_osKeyForSet.empty())
1113
0
        return nDefault;
1114
0
    CPLJSONObject object = GetObj(osName);
1115
0
    return object.ToInteger(nDefault);
1116
0
}
1117
1118
/**
1119
 * Get value.
1120
 * @param  nDefault   Default value.
1121
 * @return            Integer value.
1122
 *
1123
 */
1124
int CPLJSONObject::ToInteger(int nDefault) const
1125
0
{
1126
0
    if (!m_osKeyForSet.empty())
1127
0
        return nDefault;
1128
0
    if( m_poJsonObject /*&& json_object_get_type( TO_JSONOBJ(m_poJsonObject) ) ==
1129
0
            json_type_int*/ )
1130
0
        return json_object_get_int(TO_JSONOBJ(m_poJsonObject));
1131
0
    return nDefault;
1132
0
}
1133
1134
/**
1135
 * Get value by key.
1136
 * @param  osName    Key name.
1137
 * @param  nDefault   Default value.
1138
 * @return            Long value.
1139
 *
1140
 */
1141
GInt64 CPLJSONObject::GetLong(const std::string &osName, GInt64 nDefault) const
1142
0
{
1143
0
    if (!m_osKeyForSet.empty())
1144
0
        return nDefault;
1145
0
    CPLJSONObject object = GetObj(osName);
1146
0
    return object.ToLong(nDefault);
1147
0
}
1148
1149
/**
1150
 * Get value.
1151
 * @param  nDefault   Default value.
1152
 * @return            Long value.
1153
 *
1154
 */
1155
GInt64 CPLJSONObject::ToLong(GInt64 nDefault) const
1156
0
{
1157
0
    if (!m_osKeyForSet.empty())
1158
0
        return nDefault;
1159
0
    if( m_poJsonObject /*&& json_object_get_type( TO_JSONOBJ(m_poJsonObject) ) ==
1160
0
            json_type_int*/ )
1161
0
        return static_cast<GInt64>(
1162
0
            json_object_get_int64(TO_JSONOBJ(m_poJsonObject)));
1163
0
    return nDefault;
1164
0
}
1165
1166
/**
1167
 * Get value by key.
1168
 * @param  osName    Key name.
1169
 * @param  nDefault   Default value.
1170
 * @return            uint64_t value.
1171
 *
1172
 */
1173
uint64_t CPLJSONObject::GetUInt64(const std::string &osName,
1174
                                  uint64_t nDefault) const
1175
0
{
1176
0
    if (!m_osKeyForSet.empty())
1177
0
        return nDefault;
1178
0
    CPLJSONObject object = GetObj(osName);
1179
0
    return object.ToUInt64(nDefault);
1180
0
}
1181
1182
/**
1183
 * Get value.
1184
 * @param  nDefault   Default value.
1185
 * @return            uint64_t value.
1186
 *
1187
 */
1188
uint64_t CPLJSONObject::ToUInt64(uint64_t nDefault) const
1189
0
{
1190
0
    if (!m_osKeyForSet.empty())
1191
0
        return nDefault;
1192
0
    if( m_poJsonObject /*&& json_object_get_type( TO_JSONOBJ(m_poJsonObject) ) ==
1193
0
            json_type_int*/ )
1194
0
    {
1195
#if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_014)
1196
        // We can't do much better without json_object_get_uint64
1197
        const double v = json_object_get_double(TO_JSONOBJ(m_poJsonObject));
1198
        if (v > 0)
1199
        {
1200
            return static_cast<uint64_t>(
1201
                json_object_get_int64(TO_JSONOBJ(m_poJsonObject)));
1202
        }
1203
#else
1204
0
        return json_object_get_uint64(TO_JSONOBJ(m_poJsonObject));
1205
0
#endif
1206
0
    }
1207
0
    return nDefault;
1208
0
}
1209
1210
/**
1211
 * Get value by key.
1212
 * @param  osName    Key name.
1213
 * @param  bDefault   Default value.
1214
 * @return            Boolean value.
1215
 *
1216
 */
1217
bool CPLJSONObject::GetBool(const std::string &osName, bool bDefault) const
1218
0
{
1219
0
    if (!m_osKeyForSet.empty())
1220
0
        return bDefault;
1221
0
    CPLJSONObject object = GetObj(osName);
1222
0
    return object.ToBool(bDefault);
1223
0
}
1224
1225
/**
1226
 * \brief Get json object children.
1227
 *
1228
 * This function is useful when keys is not know and need to
1229
 * iterate over json object items and get keys and values.
1230
 *
1231
 * @return Array of CPLJSONObject class instance.
1232
 *
1233
 */
1234
std::vector<CPLJSONObject> CPLJSONObject::GetChildren() const
1235
0
{
1236
0
    std::vector<CPLJSONObject> aoChildren;
1237
0
    if (!m_osKeyForSet.empty())
1238
0
        return aoChildren;
1239
0
    if (nullptr == m_poJsonObject ||
1240
0
        json_object_get_type(TO_JSONOBJ(m_poJsonObject)) != json_type_object)
1241
0
    {
1242
0
        return aoChildren;
1243
0
    }
1244
1245
0
    json_object_iter it;
1246
0
    it.key = nullptr;
1247
0
    it.val = nullptr;
1248
0
    it.entry = nullptr;
1249
    // cppcheck-suppress cstyleCast
1250
0
    json_object_object_foreachC(TO_JSONOBJ(m_poJsonObject), it)
1251
0
    {
1252
0
        aoChildren.push_back(CPLJSONObject(it.key, it.val));
1253
0
    }
1254
1255
0
    return aoChildren;
1256
0
}
1257
1258
/**
1259
 * Get value.
1260
 * @param  bDefault   Default value.
1261
 * @return            Boolean value.
1262
 *
1263
 */
1264
bool CPLJSONObject::ToBool(bool bDefault) const
1265
0
{
1266
0
    if (!m_osKeyForSet.empty())
1267
0
        return bDefault;
1268
0
    if( m_poJsonObject /*&& json_object_get_type( TO_JSONOBJ(m_poJsonObject) ) ==
1269
0
            json_type_boolean*/ )
1270
0
        return json_object_get_boolean(TO_JSONOBJ(m_poJsonObject)) == 1;
1271
0
    return bDefault;
1272
0
}
1273
1274
/**
1275
 * Get value.
1276
 * @return            Array
1277
 *
1278
 */
1279
CPLJSONArray CPLJSONObject::ToArray() const
1280
0
{
1281
0
    if (m_osKeyForSet.empty() && m_poJsonObject &&
1282
0
        json_object_get_type(TO_JSONOBJ(m_poJsonObject)) == json_type_array)
1283
0
        return CPLJSONArray("", TO_JSONOBJ(m_poJsonObject));
1284
0
    return CPLJSONArray(INVALID_OBJ_KEY, nullptr);
1285
0
}
1286
1287
/**
1288
 * Stringify object to json format.
1289
 * @param  eFormat Format type,
1290
 * @return         A string in JSON format.
1291
 *
1292
 */
1293
std::string CPLJSONObject::Format(PrettyFormat eFormat) const
1294
0
{
1295
0
    if (m_poJsonObject)
1296
0
    {
1297
0
        const char *pszFormatString = nullptr;
1298
0
        switch (eFormat)
1299
0
        {
1300
0
            case PrettyFormat::Spaced:
1301
0
                pszFormatString = json_object_to_json_string_ext(
1302
0
                    TO_JSONOBJ(m_poJsonObject), JSON_C_TO_STRING_SPACED);
1303
0
                break;
1304
0
            case PrettyFormat::Pretty:
1305
0
                pszFormatString = json_object_to_json_string_ext(
1306
0
                    TO_JSONOBJ(m_poJsonObject), JSON_C_TO_STRING_PRETTY);
1307
0
                break;
1308
0
            default:
1309
0
                pszFormatString = json_object_to_json_string_ext(
1310
0
                    TO_JSONOBJ(m_poJsonObject), JSON_C_TO_STRING_PLAIN);
1311
0
        }
1312
0
        if (nullptr != pszFormatString)
1313
0
        {
1314
0
            return pszFormatString;
1315
0
        }
1316
0
    }
1317
0
    return "";
1318
0
}
1319
1320
/*! @cond Doxygen_Suppress */
1321
CPLJSONObject CPLJSONObject::GetObjectByPath(const std::string &osPath,
1322
                                             std::string &osName) const
1323
0
{
1324
0
    json_object *poVal = nullptr;
1325
1326
    // Typically for keys that contain / character
1327
0
    if (json_object_object_get_ex(TO_JSONOBJ(GetInternalHandle()),
1328
0
                                  osPath.c_str(), &poVal))
1329
0
    {
1330
0
        osName = osPath;
1331
0
        return *this;
1332
0
    }
1333
1334
0
    CPLStringList pathPortions(
1335
0
        CSLTokenizeString2(osPath.c_str(), JSON_PATH_DELIMITER, 0));
1336
0
    int portionsCount = pathPortions.size();
1337
0
    if (portionsCount > 100)
1338
0
    {
1339
0
        CPLError(CE_Failure, CPLE_NotSupported, "Too many components in path");
1340
0
        return CPLJSONObject(INVALID_OBJ_KEY, nullptr);
1341
0
    }
1342
0
    if (0 == portionsCount)
1343
0
        return CPLJSONObject(INVALID_OBJ_KEY, nullptr);
1344
0
    CPLJSONObject object = *this;
1345
0
    for (int i = 0; i < portionsCount - 1; ++i)
1346
0
    {
1347
        // TODO: check array index in path - i.e.
1348
        // settings/catalog/root/id:1/name if EQUALN(pathPortions[i+1], "id:",
1349
        // 3) -> getArray
1350
0
        if (json_object_object_get_ex(TO_JSONOBJ(object.GetInternalHandle()),
1351
0
                                      pathPortions[i], &poVal))
1352
0
        {
1353
0
            object = CPLJSONObject(pathPortions[i], poVal);
1354
0
        }
1355
0
        else
1356
0
        {
1357
0
            if (json_object_get_type(TO_JSONOBJ(object.m_poJsonObject)) !=
1358
0
                json_type_object)
1359
0
            {
1360
0
                return CPLJSONObject(INVALID_OBJ_KEY, nullptr);
1361
0
            }
1362
0
            object = CPLJSONObject(pathPortions[i], object);
1363
0
        }
1364
0
    }
1365
1366
    //    // Check if such object already  exists
1367
    //    if(json_object_object_get_ex(object.m_jsonObject,
1368
    //                                 pathPortions[portionsCount - 1], &poVal))
1369
    //        return JSONObject(nullptr);
1370
    //
1371
0
    osName = pathPortions[portionsCount - 1];
1372
0
    return object;
1373
0
}
1374
1375
/*! @endcond */
1376
1377
/**
1378
 * Get json object type.
1379
 * @return Json object type.
1380
 *
1381
 */
1382
CPLJSONObject::Type CPLJSONObject::GetType() const
1383
0
{
1384
0
    if (!m_osKeyForSet.empty())
1385
0
        return CPLJSONObject::Type::Unknown;
1386
1387
0
    if (nullptr == m_poJsonObject)
1388
0
    {
1389
0
        if (m_osKey == INVALID_OBJ_KEY)
1390
0
            return CPLJSONObject::Type::Unknown;
1391
0
        return CPLJSONObject::Type::Null;
1392
0
    }
1393
0
    auto jsonObj(TO_JSONOBJ(m_poJsonObject));
1394
0
    switch (json_object_get_type(jsonObj))
1395
0
    {
1396
0
        case json_type_boolean:
1397
0
            return CPLJSONObject::Type::Boolean;
1398
0
        case json_type_double:
1399
0
            return CPLJSONObject::Type::Double;
1400
0
        case json_type_int:
1401
0
        {
1402
0
            if (CPL_INT64_FITS_ON_INT32(json_object_get_int64(jsonObj)))
1403
0
                return CPLJSONObject::Type::Integer;
1404
0
            else
1405
0
                return CPLJSONObject::Type::Long;
1406
0
        }
1407
0
        case json_type_object:
1408
0
            return CPLJSONObject::Type::Object;
1409
0
        case json_type_array:
1410
0
            return CPLJSONObject::Type::Array;
1411
0
        case json_type_string:
1412
0
            return CPLJSONObject::Type::String;
1413
0
        default:
1414
0
            break;
1415
0
    }
1416
0
    return CPLJSONObject::Type::Unknown;
1417
0
}
1418
1419
/**
1420
 * Check if json object valid.
1421
 * @return true if json object valid.
1422
 *
1423
 */
1424
bool CPLJSONObject::IsValid() const
1425
0
{
1426
0
    return m_osKeyForSet.empty() && m_osKey != INVALID_OBJ_KEY;
1427
0
}
1428
1429
/**
1430
 * Decrement reference counter and make pointer NULL.
1431
 * A json object will become invalid.
1432
 *
1433
 */
1434
void CPLJSONObject::Deinit()
1435
0
{
1436
0
    if (m_poJsonObject)
1437
0
    {
1438
0
        json_object_put(TO_JSONOBJ(m_poJsonObject));
1439
0
        m_poJsonObject = nullptr;
1440
0
    }
1441
0
    m_osKey = INVALID_OBJ_KEY;
1442
0
}
1443
1444
//------------------------------------------------------------------------------
1445
// JSONArray
1446
//------------------------------------------------------------------------------
1447
/*! @cond Doxygen_Suppress */
1448
CPLJSONArray::CPLJSONArray()
1449
0
{
1450
0
    json_object_put(TO_JSONOBJ(m_poJsonObject));
1451
0
    m_poJsonObject = json_object_new_array();
1452
0
}
1453
1454
CPLJSONArray::CPLJSONArray(const std::string &osName)
1455
0
    : CPLJSONObject(osName, json_object_new_array())
1456
0
{
1457
0
    json_object_put(TO_JSONOBJ(m_poJsonObject));
1458
0
}
1459
1460
CPLJSONArray::CPLJSONArray(const std::string &osName, JSONObjectH poJsonObject)
1461
0
    : CPLJSONObject(osName, poJsonObject)
1462
0
{
1463
0
}
1464
1465
0
CPLJSONArray::CPLJSONArray(const CPLJSONObject &other) : CPLJSONObject(other)
1466
0
{
1467
0
}
1468
1469
/*! @endcond */
1470
1471
/**
1472
 * Get array size.
1473
 * @return Array size.
1474
 *
1475
 */
1476
int CPLJSONArray::Size() const
1477
0
{
1478
0
    if (m_poJsonObject)
1479
0
        return static_cast<int>(
1480
0
            json_object_array_length(TO_JSONOBJ(m_poJsonObject)));
1481
0
    return 0;
1482
0
}
1483
1484
/**
1485
 * Add null object to array.
1486
 *
1487
 * @since GDAL 3.8
1488
 */
1489
void CPLJSONArray::AddNull()
1490
0
{
1491
0
    if (m_poJsonObject)
1492
0
        json_object_array_add(TO_JSONOBJ(m_poJsonObject), nullptr);
1493
0
}
1494
1495
/**
1496
 * Add json object to array.
1497
 * @param oValue Json array.
1498
 *
1499
 */
1500
void CPLJSONArray::Add(const CPLJSONObject &oValue)
1501
0
{
1502
0
    if (m_poJsonObject && oValue.m_poJsonObject)
1503
0
        json_object_array_add(
1504
0
            TO_JSONOBJ(m_poJsonObject),
1505
0
            json_object_get(TO_JSONOBJ(oValue.m_poJsonObject)));
1506
0
}
1507
1508
/**
1509
 * Add value to array
1510
 * @param osValue Value to add.
1511
 *
1512
 */
1513
void CPLJSONArray::Add(const std::string &osValue)
1514
0
{
1515
0
    if (m_poJsonObject)
1516
0
        json_object_array_add(TO_JSONOBJ(m_poJsonObject),
1517
0
                              json_object_new_string(osValue.c_str()));
1518
0
}
1519
1520
/**
1521
 * Add value to array
1522
 * @param svValue Value to add.
1523
 * @since 3.13
1524
 *
1525
 */
1526
void CPLJSONArray::Add(std::string_view svValue)
1527
0
{
1528
0
    if (svValue.size() > static_cast<size_t>(INT_MAX - 1))
1529
0
    {
1530
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long string view");
1531
0
        return;
1532
0
    }
1533
0
    if (m_poJsonObject)
1534
0
    {
1535
0
        json_object_array_add(
1536
0
            TO_JSONOBJ(m_poJsonObject),
1537
0
            json_object_new_string_len(svValue.data(),
1538
0
                                       static_cast<int>(svValue.size())));
1539
0
    }
1540
0
}
1541
1542
/**
1543
 * Add value to array
1544
 * @param pszValue Value to add.
1545
 *
1546
 */
1547
void CPLJSONArray::Add(const char *pszValue)
1548
0
{
1549
0
    if (nullptr == pszValue)
1550
0
        return;
1551
0
    if (m_poJsonObject)
1552
0
        json_object_array_add(TO_JSONOBJ(m_poJsonObject),
1553
0
                              json_object_new_string(pszValue));
1554
0
}
1555
1556
/**
1557
 * Add value to array
1558
 * @param dfValue Value to add.
1559
 *
1560
 */
1561
void CPLJSONArray::Add(double dfValue)
1562
0
{
1563
0
    if (m_poJsonObject)
1564
0
        json_object_array_add(TO_JSONOBJ(m_poJsonObject),
1565
0
                              json_object_new_double(dfValue));
1566
0
}
1567
1568
/**
1569
 * Add value to array
1570
 * @param nValue Value to add.
1571
 *
1572
 */
1573
void CPLJSONArray::Add(int nValue)
1574
0
{
1575
0
    if (m_poJsonObject)
1576
0
        json_object_array_add(TO_JSONOBJ(m_poJsonObject),
1577
0
                              json_object_new_int(nValue));
1578
0
}
1579
1580
/**
1581
 * Add value to array
1582
 * @param nValue Value to add.
1583
 *
1584
 */
1585
void CPLJSONArray::Add(GInt64 nValue)
1586
0
{
1587
0
    if (m_poJsonObject)
1588
0
        json_object_array_add(TO_JSONOBJ(m_poJsonObject),
1589
0
                              json_object_new_int64(nValue));
1590
0
}
1591
1592
/**
1593
 * Add value to array
1594
 * @param nValue Value to add.
1595
 *
1596
 * @since GDAL 3.8
1597
 */
1598
void CPLJSONArray::Add(uint64_t nValue)
1599
0
{
1600
0
    if (m_poJsonObject)
1601
0
    {
1602
0
        json_object_array_add(TO_JSONOBJ(m_poJsonObject),
1603
0
                              json_object_new_uint64(nValue));
1604
0
    }
1605
0
}
1606
1607
/**
1608
 * Add value to array
1609
 * @param bValue Value to add.
1610
 *
1611
 */
1612
void CPLJSONArray::Add(bool bValue)
1613
0
{
1614
0
    if (m_poJsonObject)
1615
0
        json_object_array_add(TO_JSONOBJ(m_poJsonObject),
1616
0
                              json_object_new_boolean(bValue));
1617
0
}
1618
1619
/**
1620
 * Get array item by index.
1621
 * @param  nIndex Item index.
1622
 * @return        Json object.
1623
 *
1624
 */
1625
CPLJSONObject CPLJSONArray::operator[](int nIndex)
1626
0
{
1627
0
    return CPLJSONObject(
1628
0
        CPLSPrintf("id:%d", nIndex),
1629
0
        json_object_array_get_idx(TO_JSONOBJ(m_poJsonObject), nIndex));
1630
0
}
1631
1632
/**
1633
 * Get array const item by index.
1634
 * @param  nIndex Item index.
1635
 * @return        Json object.
1636
 *
1637
 */
1638
const CPLJSONObject CPLJSONArray::operator[](int nIndex) const
1639
0
{
1640
0
    return CPLJSONObject(
1641
0
        CPLSPrintf("id:%d", nIndex),
1642
0
        json_object_array_get_idx(TO_JSONOBJ(m_poJsonObject), nIndex));
1643
0
}
1644
1645
/**
1646
 * Get array item by index.
1647
 * @param  nIndex Item index.
1648
 * @return        Json object.
1649
 *
1650
 */
1651
CPLJSONObject CPLJSONArray::operator[](size_t nIndex)
1652
0
{
1653
0
    return CPLJSONObject(CPLSPrintf("id:%d", static_cast<int>(nIndex)),
1654
0
                         json_object_array_get_idx(TO_JSONOBJ(m_poJsonObject),
1655
0
                                                   static_cast<int>(nIndex)));
1656
0
}
1657
1658
/**
1659
 * Get array const item by index.
1660
 * @param  nIndex Item index.
1661
 * @return        Json object.
1662
 *
1663
 */
1664
const CPLJSONObject CPLJSONArray::operator[](size_t nIndex) const
1665
0
{
1666
0
    return CPLJSONObject(CPLSPrintf("id:%d", static_cast<int>(nIndex)),
1667
0
                         json_object_array_get_idx(TO_JSONOBJ(m_poJsonObject),
1668
0
                                                   static_cast<int>(nIndex)));
1669
0
}
1670
1671
/************************************************************************/
1672
/*                        CPLParseKeyValueJson()                        */
1673
/************************************************************************/
1674
1675
/** Return a string list of key/value pairs extracted from a JSON doc.
1676
1677
    We are expecting simple documents with key:value pairs, like the
1678
    following with no hierarchy or complex structure.
1679
    \verbatim
1680
    {
1681
      "Code" : "Success",
1682
      "LastUpdated" : "2017-07-03T16:20:17Z",
1683
      "Type" : "AWS-HMAC",
1684
      "AccessKeyId" : "bla",
1685
      "SecretAccessKey" : "bla",
1686
      "Token" : "bla",
1687
      "Expiration" : "2017-07-03T22:42:58Z"
1688
    }
1689
    \endverbatim
1690
    @since GDAL 3.7
1691
 */
1692
CPLStringList CPLParseKeyValueJson(const char *pszJson)
1693
0
{
1694
0
    CPLJSONDocument oDoc;
1695
0
    CPLStringList oNameValue;
1696
0
    if (pszJson != nullptr && oDoc.LoadMemory(pszJson))
1697
0
    {
1698
0
        for (const auto &obj : oDoc.GetRoot().GetChildren())
1699
0
        {
1700
0
            const auto eType = obj.GetType();
1701
0
            if (eType == CPLJSONObject::Type::String ||
1702
0
                eType == CPLJSONObject::Type::Integer ||
1703
0
                eType == CPLJSONObject::Type::Double)
1704
0
            {
1705
0
                oNameValue.SetNameValue(obj.GetName().c_str(),
1706
0
                                        obj.ToString().c_str());
1707
0
            }
1708
0
        }
1709
0
    }
1710
0
    return oNameValue;
1711
0
}