Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrlibjsonutils.cpp
Line
Count
Source (jump to first uncovered line)
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
6.17M
{
21
6.17M
    if (ppoObj == nullptr)
22
0
        return false;
23
6.17M
    json_tokener *jstok = json_tokener_new();
24
6.17M
    const int nLen = pszText == nullptr ? 0 : static_cast<int>(strlen(pszText));
25
6.17M
    *ppoObj = json_tokener_parse_ex(jstok, pszText, nLen);
26
6.17M
    if (jstok->err != json_tokener_success)
27
4.50M
    {
28
4.50M
        if (bVerboseError)
29
4.47M
        {
30
4.47M
            CPLError(CE_Failure, CPLE_AppDefined,
31
4.47M
                     "JSON parsing error: %s (at offset %d)",
32
4.47M
                     json_tokener_error_desc(jstok->err), jstok->char_offset);
33
4.47M
        }
34
35
4.50M
        json_tokener_free(jstok);
36
4.50M
        *ppoObj = nullptr;
37
4.50M
        return false;
38
4.50M
    }
39
1.67M
    json_tokener_free(jstok);
40
1.67M
    return true;
41
6.17M
}
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.22M
{
53
1.22M
    json_object *poRet = nullptr;
54
1.22M
    json_object_object_get_ex(obj, key, &poRet);
55
1.22M
    return poRet;
56
1.22M
}
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.51M
{
95
3.51M
    if (nullptr == pszName || nullptr == poObj)
96
1.99k
        return nullptr;
97
98
3.51M
    if (nullptr != json_object_get_object(poObj))
99
3.50M
    {
100
3.50M
        lh_entry *entry = json_object_get_object(poObj)->head;
101
7.02M
        while (entry != nullptr)
102
5.87M
        {
103
5.87M
            if (EQUAL(static_cast<const char *>(entry->k), pszName))
104
2.35M
                return entry;
105
3.51M
            entry = entry->next;
106
3.51M
        }
107
3.50M
    }
108
109
1.15M
    return nullptr;
110
3.51M
}
111
112
json_object *OGRGeoJSONFindMemberByName(json_object *poObj, const char *pszName)
113
2.78M
{
114
2.78M
    lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, pszName);
115
2.78M
    if (nullptr == entry)
116
1.00M
        return nullptr;
117
1.77M
    return static_cast<json_object *>(const_cast<void *>(entry->v));
118
2.78M
}
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
736k
{
129
736k
    const void *userData =
130
#if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
131
        jso->_userdata;
132
#else
133
736k
        json_object_get_userdata(jso);
134
736k
#endif
135
    // Precision is stored as a uintptr_t content casted to void*
136
736k
    const uintptr_t nPrecisionIn = reinterpret_cast<uintptr_t>(userData);
137
736k
    const double dfVal = json_object_get_double(jso);
138
736k
    if (fabs(dfVal) > 1e50 && !std::isinf(dfVal))
139
21.6k
    {
140
21.6k
        char szBuffer[75] = {};
141
21.6k
        const size_t nLen =
142
21.6k
            CPLsnprintf(szBuffer, sizeof(szBuffer), "%.17g", dfVal);
143
21.6k
        return printbuf_memappend(pb, szBuffer, static_cast<int>(nLen));
144
21.6k
    }
145
715k
    else
146
715k
    {
147
715k
        const bool bPrecisionIsNegative =
148
715k
            (nPrecisionIn >> (8 * sizeof(nPrecisionIn) - 1)) != 0;
149
715k
        const int nPrecision =
150
715k
            bPrecisionIsNegative ? 15 : static_cast<int>(nPrecisionIn);
151
715k
        OGRWktOptions opts(nPrecision, /* round = */ true);
152
715k
        opts.format = OGRWktFormat::F;
153
154
715k
        const std::string s = OGRFormatDouble(dfVal, opts, 1);
155
156
715k
        return printbuf_memappend(pb, s.data(), static_cast<int>(s.size()));
157
715k
    }
158
736k
}
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
801k
{
167
801k
    json_object *jso = json_object_new_double(dfVal);
168
801k
    json_object_set_serializer(
169
801k
        jso, OGR_json_double_with_precision_to_string,
170
801k
        reinterpret_cast<void *>(static_cast<uintptr_t>(nCoordPrecision)),
171
801k
        nullptr);
172
801k
    return jso;
173
801k
}
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
5.62k
{
183
5.62k
    char szBuffer[75] = {};
184
5.62k
    int nSize = 0;
185
5.62k
    const double dfVal = json_object_get_double(jso);
186
5.62k
    if (std::isnan(dfVal))
187
0
        nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "NaN");
188
5.62k
    else if (std::isinf(dfVal))
189
220
    {
190
220
        if (dfVal > 0)
191
67
            nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "Infinity");
192
153
        else
193
153
            nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), "-Infinity");
194
220
    }
195
5.40k
    else
196
5.40k
    {
197
5.40k
        char szFormatting[32] = {};
198
5.40k
        const void *userData =
199
#if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
200
            jso->_userdata;
201
#else
202
5.40k
            json_object_get_userdata(jso);
203
5.40k
#endif
204
5.40k
        const uintptr_t nSignificantFigures =
205
5.40k
            reinterpret_cast<uintptr_t>(userData);
206
5.40k
        const bool bSignificantFiguresIsNegative =
207
5.40k
            (nSignificantFigures >> (8 * sizeof(nSignificantFigures) - 1)) != 0;
208
5.40k
        const int nInitialSignificantFigures =
209
5.40k
            bSignificantFiguresIsNegative
210
5.40k
                ? 17
211
5.40k
                : static_cast<int>(nSignificantFigures);
212
5.40k
        CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%dg",
213
5.40k
                    nInitialSignificantFigures);
214
5.40k
        nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), szFormatting, dfVal);
215
5.40k
        const char *pszDot = strchr(szBuffer, '.');
216
217
        // Try to avoid .xxxx999999y or .xxxx000000y rounding issues by
218
        // decreasing a bit precision.
219
5.40k
        if (nInitialSignificantFigures > 10 && pszDot != nullptr &&
220
5.40k
            (strstr(pszDot, "999999") != nullptr ||
221
1.95k
             strstr(pszDot, "000000") != nullptr))
222
1.31k
        {
223
1.31k
            bool bOK = false;
224
1.84k
            for (int i = 1; i <= 3; i++)
225
1.72k
            {
226
1.72k
                CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%dg",
227
1.72k
                            nInitialSignificantFigures - i);
228
1.72k
                nSize = CPLsnprintf(szBuffer, sizeof(szBuffer), szFormatting,
229
1.72k
                                    dfVal);
230
1.72k
                pszDot = strchr(szBuffer, '.');
231
1.72k
                if (pszDot != nullptr && strstr(pszDot, "999999") == nullptr &&
232
1.72k
                    strstr(pszDot, "000000") == nullptr)
233
1.19k
                {
234
1.19k
                    bOK = true;
235
1.19k
                    break;
236
1.19k
                }
237
1.72k
            }
238
1.31k
            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.31k
        }
246
247
5.40k
        if (nSize + 2 < static_cast<int>(sizeof(szBuffer)) &&
248
5.40k
            strchr(szBuffer, '.') == nullptr &&
249
5.40k
            strchr(szBuffer, 'e') == nullptr)
250
3.36k
        {
251
3.36k
            nSize +=
252
3.36k
                CPLsnprintf(szBuffer + nSize, sizeof(szBuffer) - nSize, ".0");
253
3.36k
        }
254
5.40k
    }
255
256
5.62k
    return printbuf_memappend(pb, szBuffer, nSize);
257
5.62k
}
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
110k
{
267
110k
    json_object *jso = json_object_new_double(dfVal);
268
110k
    json_object_set_serializer(
269
110k
        jso, OGR_json_double_with_significant_figures_to_string,
270
110k
        reinterpret_cast<void *>(static_cast<uintptr_t>(nSignificantFigures)),
271
110k
        nullptr);
272
110k
    return jso;
273
110k
}
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
493k
{
287
493k
    eSubType = OFSTNone;
288
289
493k
    if (poObject == nullptr)
290
1.15k
    {
291
1.15k
        return OFTString;
292
1.15k
    }
293
294
491k
    json_type type = json_object_get_type(poObject);
295
296
491k
    if (json_type_boolean == type)
297
28.5k
    {
298
28.5k
        eSubType = OFSTBoolean;
299
28.5k
        return OFTInteger;
300
28.5k
    }
301
463k
    else if (json_type_double == type)
302
25.8k
        return OFTReal;
303
437k
    else if (json_type_int == type)
304
56.7k
    {
305
56.7k
        GIntBig nVal = json_object_get_int64(poObject);
306
56.7k
        if (!CPL_INT64_FITS_ON_INT32(nVal))
307
16.5k
        {
308
16.5k
            if (nVal == MY_INT64_MIN || nVal == MY_INT64_MAX)
309
1.14k
            {
310
1.14k
                static bool bWarned = false;
311
1.14k
                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
1.14k
            }
321
16.5k
            return OFTInteger64;
322
16.5k
        }
323
40.1k
        else
324
40.1k
        {
325
40.1k
            return OFTInteger;
326
40.1k
        }
327
56.7k
    }
328
380k
    else if (json_type_string == type)
329
194k
        return OFTString;
330
186k
    else if (json_type_array == type)
331
184k
    {
332
184k
        if (bArrayAsString)
333
0
        {
334
0
            eSubType = OFSTJSON;
335
0
            return OFTString;
336
0
        }
337
184k
        const auto nSize = json_object_array_length(poObject);
338
184k
        if (nSize == 0)
339
3.82k
        {
340
3.82k
            eSubType = OFSTJSON;
341
3.82k
            return OFTString;
342
3.82k
        }
343
180k
        OGRFieldType eType = OFTIntegerList;
344
360k
        for (auto i = decltype(nSize){0}; i < nSize; i++)
345
181k
        {
346
181k
            json_object *poRow = json_object_array_get_idx(poObject, i);
347
181k
            if (poRow != nullptr)
348
180k
            {
349
180k
                type = json_object_get_type(poRow);
350
180k
                if (type == json_type_string)
351
103k
                {
352
103k
                    if (i == 0 || eType == OFTStringList)
353
103k
                    {
354
103k
                        eType = OFTStringList;
355
103k
                    }
356
0
                    else
357
0
                    {
358
0
                        eSubType = OFSTJSON;
359
0
                        return OFTString;
360
0
                    }
361
103k
                }
362
77.5k
                else if (type == json_type_double)
363
14.6k
                {
364
14.6k
                    if (eSubType == OFSTNone &&
365
14.6k
                        (i == 0 || eType == OFTRealList ||
366
14.6k
                         eType == OFTIntegerList || eType == OFTInteger64List))
367
14.6k
                    {
368
14.6k
                        eType = OFTRealList;
369
14.6k
                    }
370
0
                    else
371
0
                    {
372
0
                        eSubType = OFSTJSON;
373
0
                        return OFTString;
374
0
                    }
375
14.6k
                }
376
62.8k
                else if (type == json_type_int)
377
40.7k
                {
378
40.7k
                    if (eSubType == OFSTNone && eType == OFTIntegerList)
379
40.4k
                    {
380
40.4k
                        GIntBig nVal = json_object_get_int64(poRow);
381
40.4k
                        if (!CPL_INT64_FITS_ON_INT32(nVal))
382
14.3k
                            eType = OFTInteger64List;
383
40.4k
                    }
384
348
                    else if (eSubType == OFSTNone &&
385
348
                             (eType == OFTInteger64List ||
386
348
                              eType == OFTRealList))
387
348
                    {
388
                        // ok
389
348
                    }
390
0
                    else
391
0
                    {
392
0
                        eSubType = OFSTJSON;
393
0
                        return OFTString;
394
0
                    }
395
40.7k
                }
396
22.0k
                else if (type == json_type_boolean)
397
21.3k
                {
398
21.3k
                    if (i == 0 ||
399
21.3k
                        (eType == OFTIntegerList && eSubType == OFSTBoolean))
400
21.3k
                    {
401
21.3k
                        eSubType = OFSTBoolean;
402
21.3k
                    }
403
0
                    else
404
0
                    {
405
0
                        eSubType = OFSTJSON;
406
0
                        return OFTString;
407
0
                    }
408
21.3k
                }
409
691
                else
410
691
                {
411
691
                    eSubType = OFSTJSON;
412
691
                    return OFTString;
413
691
                }
414
180k
            }
415
297
            else
416
297
            {
417
297
                eSubType = OFSTJSON;
418
297
                return OFTString;
419
297
            }
420
181k
        }
421
422
179k
        return eType;
423
180k
    }
424
2.50k
    else if (json_type_object == type)
425
2.50k
    {
426
2.50k
        eSubType = OFSTJSON;
427
2.50k
        return OFTString;
428
2.50k
    }
429
430
0
    return OFTString;  // null
431
491k
}
432
433
/*! @endcond */