Coverage Report

Created: 2025-12-03 08:24

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