LCOV - code coverage report
Current view: top level - ogr - ogrlibjsonutils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 172 180 95.6 %
Date: 2024-11-21 22:18:42 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_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 */

Generated by: LCOV version 1.14