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