Coverage Report

Created: 2025-06-09 07:43

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