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