LCOV - code coverage report
Current view: top level - ogr - ogrlibjsonutils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 179 187 95.7 %
Date: 2026-10-02 21:07:22 Functions: 10 10 100.0 %

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

Generated by: LCOV version 1.14