Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrlibjsonutils.cpp
Line
Count
Source
1
// SPDX-License-Identifier: MIT
2
// Copyright 2007, Mateusz Loskot
3
// Copyright 2008-2024, Even Rouault <even.rouault at spatialys.com>
4
5
/*! @cond Doxygen_Suppress */
6
7
#include "ogrlibjsonutils.h"
8
9
#include "cpl_string.h"
10
#include "ogr_geometry.h"
11
#include "ogr_p.h"
12
13
#include <cmath>
14
15
/************************************************************************/
16
/*                            OGRJSonParse()                            */
17
/************************************************************************/
18
19
bool OGRJSonParse(const char *pszText, json_object **ppoObj, bool bVerboseError,
20
                  int nInputLength)
21
0
{
22
0
    if (ppoObj == nullptr)
23
0
        return false;
24
0
    json_tokener *jstok = json_tokener_new();
25
0
    const int nLen =
26
0
        nInputLength != -1
27
0
            ? nInputLength
28
0
            : (pszText == nullptr ? 0 : static_cast<int>(strlen(pszText)));
29
0
    *ppoObj = json_tokener_parse_ex(jstok, pszText, nLen);
30
0
    if (jstok->err != json_tokener_success)
31
0
    {
32
0
        if (bVerboseError)
33
0
        {
34
0
            CPLError(CE_Failure, CPLE_AppDefined,
35
0
                     "JSON parsing error: %s (at offset %d)",
36
0
                     json_tokener_error_desc(jstok->err), jstok->char_offset);
37
0
        }
38
39
0
        json_tokener_free(jstok);
40
0
        *ppoObj = nullptr;
41
0
        return false;
42
0
    }
43
0
    json_tokener_free(jstok);
44
0
    return true;
45
0
}
46
47
/************************************************************************/
48
/*                     CPL_json_object_object_get()                     */
49
/************************************************************************/
50
51
// This is the same as json_object_object_get() except it will not raise
52
// deprecation warning.
53
54
json_object *CPL_json_object_object_get(struct json_object *obj,
55
                                        const char *key)
56
0
{
57
0
    json_object *poRet = nullptr;
58
0
    CPL_IGNORE_RET_VAL(json_object_object_get_ex(obj, key, &poRet));
59
0
    return poRet;
60
0
}
61
62
/************************************************************************/
63
/*                     json_ex_get_object_by_path()                     */
64
/************************************************************************/
65
66
json_object *json_ex_get_object_by_path(json_object *poObj, const char *pszPath)
67
0
{
68
0
    if (poObj == nullptr || json_object_get_type(poObj) != json_type_object ||
69
0
        pszPath == nullptr || *pszPath == '\0')
70
0
    {
71
0
        return nullptr;
72
0
    }
73
0
    char **papszTokens = CSLTokenizeString2(pszPath, ".", 0);
74
0
    for (int i = 0; papszTokens[i] != nullptr; i++)
75
0
    {
76
0
        poObj = CPL_json_object_object_get(poObj, papszTokens[i]);
77
0
        if (poObj == nullptr)
78
0
            break;
79
0
        if (papszTokens[i + 1] != nullptr)
80
0
        {
81
0
            if (json_object_get_type(poObj) != json_type_object)
82
0
            {
83
0
                poObj = nullptr;
84
0
                break;
85
0
            }
86
0
        }
87
0
    }
88
0
    CSLDestroy(papszTokens);
89
0
    return poObj;
90
0
}
91
92
/************************************************************************/
93
/*                      OGRGeoJSONFindMemberByName                      */
94
/************************************************************************/
95
96
lh_entry *OGRGeoJSONFindMemberEntryByName(json_object *poObj,
97
                                          const char *pszName)
98
0
{
99
0
    if (nullptr == pszName || nullptr == poObj)
100
0
        return nullptr;
101
102
0
    if (nullptr != json_object_get_object(poObj))
103
0
    {
104
0
        lh_entry *entry = json_object_get_object(poObj)->head;
105
0
        while (entry != nullptr)
106
0
        {
107
0
            if (EQUAL(static_cast<const char *>(entry->k), pszName))
108
0
                return entry;
109
0
            entry = entry->next;
110
0
        }
111
0
    }
112
113
0
    return nullptr;
114
0
}
115
116
json_object *OGRGeoJSONFindMemberByName(json_object *poObj, const char *pszName)
117
0
{
118
0
    lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, pszName);
119
0
    if (nullptr == entry)
120
0
        return nullptr;
121
0
    return static_cast<json_object *>(const_cast<void *>(entry->v));
122
0
}
123
124
/************************************************************************/
125
/*              OGR_json_double_with_precision_to_string()              */
126
/************************************************************************/
127
128
static int OGR_json_double_with_precision_to_string(struct json_object *jso,
129
                                                    struct printbuf *pb,
130
                                                    int /* level */,
131
                                                    int /* flags */)
132
0
{
133
0
    const void *userData =
134
#if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
135
        jso->_userdata;
136
#else
137
0
        json_object_get_userdata(jso);
138
0
#endif
139
    // Precision is stored as a uintptr_t content casted to void*
140
0
    const uintptr_t nPrecisionIn = reinterpret_cast<uintptr_t>(userData);
141
0
    const double dfVal = json_object_get_double(jso);
142
0
    if (fabs(dfVal) > 1e50 && !std::isinf(dfVal))
143
0
    {
144
0
        char szBuffer[75] = {};
145
0
        const size_t nLen =
146
0
            CPLsnprintf(szBuffer, sizeof(szBuffer), "%.17g", dfVal);
147
0
        return printbuf_memappend(pb, szBuffer, static_cast<int>(nLen));
148
0
    }
149
0
    else
150
0
    {
151
0
        const bool bPrecisionIsNegative =
152
0
            (nPrecisionIn >> (8 * sizeof(nPrecisionIn) - 1)) != 0;
153
0
        const int nPrecision =
154
0
            bPrecisionIsNegative ? 15 : static_cast<int>(nPrecisionIn);
155
0
        OGRWktOptions opts(nPrecision, /* round = */ true);
156
0
        opts.format = OGRWktFormat::F;
157
158
0
        const std::string s = OGRFormatDouble(dfVal, opts, 1);
159
160
0
        return printbuf_memappend(pb, s.data(), static_cast<int>(s.size()));
161
0
    }
162
0
}
163
164
/************************************************************************/
165
/*               json_object_new_double_with_precision()                */
166
/************************************************************************/
167
168
json_object *json_object_new_double_with_precision(double dfVal,
169
                                                   int nCoordPrecision)
170
0
{
171
0
    json_object *jso = json_object_new_double(dfVal);
172
0
    json_object_set_serializer(
173
0
        jso, OGR_json_double_with_precision_to_string,
174
0
        reinterpret_cast<void *>(static_cast<uintptr_t>(nCoordPrecision)),
175
0
        nullptr);
176
0
    return jso;
177
0
}
178
179
/************************************************************************/
180
/*         OGR_json_double_with_significant_figures_to_string()         */
181
/************************************************************************/
182
183
static int OGR_json_double_with_significant_figures_to_string(
184
    struct json_object *jso, struct printbuf *pb, int /* level */,
185
    int /* flags */)
186
0
{
187
0
    char szBuffer[75] = {};
188
0
    int nSize = 0;
189
0
    const double dfVal = json_object_get_double(jso);
190
0
    if (std::isnan(dfVal))
191
0
        nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "NaN");
192
0
    else if (std::isinf(dfVal))
193
0
    {
194
0
        if (dfVal > 0)
195
0
            nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "Infinity");
196
0
        else
197
0
            nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "-Infinity");
198
0
    }
199
0
    else
200
0
    {
201
0
        char szFormatting[32] = {};
202
0
        const void *userData =
203
#if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
204
            jso->_userdata;
205
#else
206
0
            json_object_get_userdata(jso);
207
0
#endif
208
0
        const uintptr_t nSignificantFigures =
209
0
            reinterpret_cast<uintptr_t>(userData);
210
0
        const bool bSignificantFiguresIsNegative =
211
0
            (nSignificantFigures >> (8 * sizeof(nSignificantFigures) - 1)) != 0;
212
0
        const int nInitialSignificantFigures =
213
0
            bSignificantFiguresIsNegative
214
0
                ? 17
215
0
                : static_cast<int>(nSignificantFigures);
216
0
        CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%dg",
217
0
                    nInitialSignificantFigures);
218
0
        nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), szFormatting, dfVal);
219
0
        const char *pszDot = strchr(szBuffer, '.');
220
221
        // Try to avoid .xxxx999999y or .xxxx000000y rounding issues by
222
        // decreasing a bit precision.
223
0
        if (nInitialSignificantFigures > 10 && pszDot != nullptr &&
224
0
            (strstr(pszDot, "999999") != nullptr ||
225
0
             strstr(pszDot, "000000") != nullptr))
226
0
        {
227
0
            bool bOK = false;
228
0
            for (int i = 1; i <= 3; i++)
229
0
            {
230
0
                CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%dg",
231
0
                            nInitialSignificantFigures - i);
232
0
                nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), szFormatting,
233
0
                                    dfVal);
234
0
                pszDot = strchr(szBuffer, '.');
235
0
                if (pszDot != nullptr && strstr(pszDot, "999999") == nullptr &&
236
0
                    strstr(pszDot, "000000") == nullptr)
237
0
                {
238
0
                    bOK = true;
239
0
                    break;
240
0
                }
241
0
            }
242
0
            if (!bOK)
243
0
            {
244
0
                CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%dg",
245
0
                            nInitialSignificantFigures);
246
0
                nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), szFormatting,
247
0
                                    dfVal);
248
0
            }
249
0
        }
250
251
0
        if (nSize + 2 < static_cast<int>(sizeof(szBuffer)) &&
252
0
            strchr(szBuffer, '.') == nullptr &&
253
0
            strchr(szBuffer, 'e') == nullptr)
254
0
        {
255
0
            nSize +=
256
0
                CPLsnprintf(szBuffer + nSize, sizeof(szBuffer) - nSize, ".0");
257
0
        }
258
0
    }
259
260
0
    return printbuf_memappend(pb, szBuffer, nSize);
261
0
}
262
263
/************************************************************************/
264
/*          json_object_new_double_with_significant_figures()           */
265
/************************************************************************/
266
267
json_object *
268
json_object_new_double_with_significant_figures(double dfVal,
269
                                                int nSignificantFigures)
270
0
{
271
0
    json_object *jso = json_object_new_double(dfVal);
272
0
    json_object_set_serializer(
273
0
        jso, OGR_json_double_with_significant_figures_to_string,
274
0
        reinterpret_cast<void *>(static_cast<uintptr_t>(nSignificantFigures)),
275
0
        nullptr);
276
0
    return jso;
277
0
}
278
279
/************************************************************************/
280
/*                     GeoJSONPropertyToFieldType()                     */
281
/************************************************************************/
282
283
constexpr GIntBig MY_INT64_MAX =
284
    (static_cast<GIntBig>(0x7FFFFFFF) << 32) | 0xFFFFFFFF;
285
constexpr GIntBig MY_INT64_MIN = static_cast<GIntBig>(0x80000000) << 32;
286
287
OGRFieldType GeoJSONPropertyToFieldType(json_object *poObject,
288
                                        OGRFieldSubType &eSubType,
289
                                        bool bArrayAsString)
290
0
{
291
0
    eSubType = OFSTNone;
292
293
0
    if (poObject == nullptr)
294
0
    {
295
0
        return OFTString;
296
0
    }
297
298
0
    json_type type = json_object_get_type(poObject);
299
300
0
    if (json_type_boolean == type)
301
0
    {
302
0
        eSubType = OFSTBoolean;
303
0
        return OFTInteger;
304
0
    }
305
0
    else if (json_type_double == type)
306
0
        return OFTReal;
307
0
    else if (json_type_int == type)
308
0
    {
309
0
        GIntBig nVal = json_object_get_int64(poObject);
310
0
        if (!CPL_INT64_FITS_ON_INT32(nVal))
311
0
        {
312
0
            if (nVal == MY_INT64_MIN || nVal == MY_INT64_MAX)
313
0
            {
314
0
                static bool bWarned = false;
315
0
                if (!bWarned)
316
0
                {
317
0
                    bWarned = true;
318
0
                    CPLError(
319
0
                        CE_Warning, CPLE_AppDefined,
320
0
                        "Integer values probably ranging out of 64bit integer "
321
0
                        "range have been found. Will be clamped to "
322
0
                        "INT64_MIN/INT64_MAX");
323
0
                }
324
0
            }
325
0
            return OFTInteger64;
326
0
        }
327
0
        else
328
0
        {
329
0
            return OFTInteger;
330
0
        }
331
0
    }
332
0
    else if (json_type_string == type)
333
0
        return OFTString;
334
0
    else if (json_type_array == type)
335
0
    {
336
0
        if (bArrayAsString)
337
0
        {
338
0
            eSubType = OFSTJSON;
339
0
            return OFTString;
340
0
        }
341
0
        const auto nSize = json_object_array_length(poObject);
342
0
        if (nSize == 0)
343
0
        {
344
0
            eSubType = OFSTJSON;
345
0
            return OFTString;
346
0
        }
347
0
        OGRFieldType eType = OFTIntegerList;
348
0
        for (auto i = decltype(nSize){0}; i < nSize; i++)
349
0
        {
350
0
            json_object *poRow = json_object_array_get_idx(poObject, i);
351
0
            if (poRow != nullptr)
352
0
            {
353
0
                type = json_object_get_type(poRow);
354
0
                if (type == json_type_string)
355
0
                {
356
0
                    if (i == 0 || eType == OFTStringList)
357
0
                    {
358
0
                        eType = OFTStringList;
359
0
                    }
360
0
                    else
361
0
                    {
362
0
                        eSubType = OFSTJSON;
363
0
                        return OFTString;
364
0
                    }
365
0
                }
366
0
                else if (type == json_type_double)
367
0
                {
368
0
                    if (eSubType == OFSTNone &&
369
0
                        (i == 0 || eType == OFTRealList ||
370
0
                         eType == OFTIntegerList || eType == OFTInteger64List))
371
0
                    {
372
0
                        eType = OFTRealList;
373
0
                    }
374
0
                    else
375
0
                    {
376
0
                        eSubType = OFSTJSON;
377
0
                        return OFTString;
378
0
                    }
379
0
                }
380
0
                else if (type == json_type_int)
381
0
                {
382
0
                    if (eSubType == OFSTNone && eType == OFTIntegerList)
383
0
                    {
384
0
                        GIntBig nVal = json_object_get_int64(poRow);
385
0
                        if (!CPL_INT64_FITS_ON_INT32(nVal))
386
0
                            eType = OFTInteger64List;
387
0
                    }
388
0
                    else if (eSubType == OFSTNone &&
389
0
                             (eType == OFTInteger64List ||
390
0
                              eType == OFTRealList))
391
0
                    {
392
                        // ok
393
0
                    }
394
0
                    else
395
0
                    {
396
0
                        eSubType = OFSTJSON;
397
0
                        return OFTString;
398
0
                    }
399
0
                }
400
0
                else if (type == json_type_boolean)
401
0
                {
402
0
                    if (i == 0 ||
403
0
                        (eType == OFTIntegerList && eSubType == OFSTBoolean))
404
0
                    {
405
0
                        eSubType = OFSTBoolean;
406
0
                    }
407
0
                    else
408
0
                    {
409
0
                        eSubType = OFSTJSON;
410
0
                        return OFTString;
411
0
                    }
412
0
                }
413
0
                else
414
0
                {
415
0
                    eSubType = OFSTJSON;
416
0
                    return OFTString;
417
0
                }
418
0
            }
419
0
            else
420
0
            {
421
0
                eSubType = OFSTJSON;
422
0
                return OFTString;
423
0
            }
424
0
        }
425
426
0
        return eType;
427
0
    }
428
0
    else if (json_type_object == type)
429
0
    {
430
0
        eSubType = OFSTJSON;
431
0
        return OFTString;
432
0
    }
433
434
0
    return OFTString;  // null
435
0
}
436
437
/*! @endcond */