LCOV - code coverage report
Current view: top level - ogr - ogrlibjsonutils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 177 185 95.7 %
Date: 2025-01-18 12:42:00 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        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 */

Generated by: LCOV version 1.14