Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/geojson/ogresrijsonreader.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implementation of OGRESRIJSONReader class (OGR ESRIJSON Driver)
5
 *           to read ESRI Feature Service REST data
6
 * Author:   Even Rouault, even dot rouault at spatialys.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
10
 * Copyright (c) 2007, Mateusz Loskot
11
 * Copyright (c) 2013, Kyle Shannon <kyle at pobox dot com>
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15
16
#include "cpl_port.h"
17
#include "ogrlibjsonutils.h"
18
19
#include <limits.h>
20
#include <stddef.h>
21
22
#include "cpl_conv.h"
23
#include "cpl_error.h"
24
#include "cpl_time.h"
25
#include "json.h"
26
// #include "json_object.h"
27
// #include "json_tokener.h"
28
#include "ogr_api.h"
29
#include "ogr_core.h"
30
#include "ogr_feature.h"
31
#include "ogr_geometry.h"
32
#include "ogr_spatialref.h"
33
#include "ogr_geojson.h"
34
#include "ogrgeojsonreader.h"
35
#include "ogrgeojsonutils.h"
36
#include "ogresrijsongeometry.h"
37
38
#include <map>
39
#include <utility>
40
41
// #include "symbol_renames.h"
42
43
/************************************************************************/
44
/*                          OGRESRIJSONReader()                         */
45
/************************************************************************/
46
47
0
OGRESRIJSONReader::OGRESRIJSONReader() : poGJObject_(nullptr), poLayer_(nullptr)
48
0
{
49
0
}
50
51
/************************************************************************/
52
/*                         ~OGRESRIJSONReader()                         */
53
/************************************************************************/
54
55
OGRESRIJSONReader::~OGRESRIJSONReader()
56
0
{
57
0
    if (nullptr != poGJObject_)
58
0
    {
59
0
        json_object_put(poGJObject_);
60
0
    }
61
62
0
    poGJObject_ = nullptr;
63
0
    poLayer_ = nullptr;
64
0
}
65
66
/************************************************************************/
67
/*                           Parse()                                    */
68
/************************************************************************/
69
70
OGRErr OGRESRIJSONReader::Parse(const char *pszText)
71
0
{
72
0
    json_object *jsobj = nullptr;
73
0
    if (nullptr != pszText && !OGRJSonParse(pszText, &jsobj, true))
74
0
    {
75
0
        return OGRERR_CORRUPT_DATA;
76
0
    }
77
78
    // JSON tree is shared for while lifetime of the reader object
79
    // and will be released in the destructor.
80
0
    poGJObject_ = jsobj;
81
0
    return OGRERR_NONE;
82
0
}
83
84
/************************************************************************/
85
/*                           ReadLayers()                               */
86
/************************************************************************/
87
88
void OGRESRIJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS,
89
                                   GeoJSONSourceType eSourceType)
90
0
{
91
0
    CPLAssert(nullptr == poLayer_);
92
93
0
    poDS->SetSupportsMGeometries(true);
94
95
0
    if (nullptr == poGJObject_)
96
0
    {
97
0
        CPLDebug("ESRIJSON",
98
0
                 "Missing parsed ESRIJSON data. Forgot to call Parse()?");
99
0
        return;
100
0
    }
101
102
0
    OGRSpatialReference *poSRS = OGRESRIJSONReadSpatialReference(poGJObject_);
103
104
0
    std::string osName = "ESRIJSON";
105
0
    if (eSourceType == eGeoJSONSourceFile)
106
0
    {
107
0
        osName = poDS->GetDescription();
108
0
        if (STARTS_WITH_CI(osName.c_str(), "ESRIJSON:"))
109
0
            osName = osName.substr(strlen("ESRIJSON:"));
110
0
        osName = CPLGetBasenameSafe(osName.c_str());
111
0
    }
112
113
0
    auto eGeomType = OGRESRIJSONGetGeometryType(poGJObject_);
114
0
    if (eGeomType == wkbNone)
115
0
    {
116
0
        if (poSRS)
117
0
        {
118
0
            eGeomType = wkbUnknown;
119
0
        }
120
0
        else
121
0
        {
122
0
            json_object *poObjFeatures =
123
0
                OGRGeoJSONFindMemberByName(poGJObject_, "features");
124
0
            if (poObjFeatures &&
125
0
                json_type_array == json_object_get_type(poObjFeatures))
126
0
            {
127
0
                const auto nFeatures = json_object_array_length(poObjFeatures);
128
0
                for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
129
0
                {
130
0
                    json_object *poObjFeature =
131
0
                        json_object_array_get_idx(poObjFeatures, i);
132
0
                    if (poObjFeature != nullptr &&
133
0
                        json_object_get_type(poObjFeature) == json_type_object)
134
0
                    {
135
0
                        if (auto poObjGeometry = OGRGeoJSONFindMemberByName(
136
0
                                poObjFeature, "geometry"))
137
0
                        {
138
0
                            eGeomType = wkbUnknown;
139
0
                            poSRS =
140
0
                                OGRESRIJSONReadSpatialReference(poObjGeometry);
141
0
                            break;
142
0
                        }
143
0
                    }
144
0
                }
145
0
            }
146
0
        }
147
0
    }
148
149
0
    poLayer_ =
150
0
        new OGRGeoJSONLayer(osName.c_str(), poSRS, eGeomType, poDS, nullptr);
151
0
    poLayer_->SetSupportsMGeometries(true);
152
0
    if (poSRS != nullptr)
153
0
        poSRS->Release();
154
155
0
    if (!GenerateLayerDefn())
156
0
    {
157
0
        CPLError(CE_Failure, CPLE_AppDefined,
158
0
                 "Layer schema generation failed.");
159
160
0
        delete poLayer_;
161
0
        return;
162
0
    }
163
164
0
    OGRGeoJSONLayer *poThisLayer = ReadFeatureCollection(poGJObject_);
165
0
    if (poThisLayer == nullptr)
166
0
    {
167
0
        delete poLayer_;
168
0
        return;
169
0
    }
170
171
0
    CPLErrorReset();
172
173
0
    poLayer_->DetectGeometryType();
174
0
    poDS->AddLayer(poLayer_);
175
0
}
176
177
/************************************************************************/
178
/*                        GenerateFeatureDefn()                         */
179
/************************************************************************/
180
181
bool OGRESRIJSONReader::GenerateLayerDefn()
182
0
{
183
0
    CPLAssert(nullptr != poGJObject_);
184
185
0
    bool bSuccess = true;
186
187
0
    OGRFeatureDefn *poDefn = poLayer_->GetLayerDefn();
188
0
    CPLAssert(nullptr != poDefn);
189
0
    CPLAssert(0 == poDefn->GetFieldCount());
190
0
    auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
191
192
    /* -------------------------------------------------------------------- */
193
    /*      Scan all features and generate layer definition.                */
194
    /* -------------------------------------------------------------------- */
195
0
    json_object *poFields = OGRGeoJSONFindMemberByName(poGJObject_, "fields");
196
0
    if (nullptr != poFields &&
197
0
        json_type_array == json_object_get_type(poFields))
198
0
    {
199
0
        const auto nFeatures = json_object_array_length(poFields);
200
0
        for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
201
0
        {
202
0
            json_object *poField = json_object_array_get_idx(poFields, i);
203
0
            if (!ParseField(poField))
204
0
            {
205
0
                CPLDebug("GeoJSON", "Create feature schema failure.");
206
0
                bSuccess = false;
207
0
            }
208
0
        }
209
0
    }
210
0
    else if ((poFields = OGRGeoJSONFindMemberByName(
211
0
                  poGJObject_, "fieldAliases")) != nullptr &&
212
0
             json_object_get_type(poFields) == json_type_object)
213
0
    {
214
0
        json_object_iter it;
215
0
        it.key = nullptr;
216
0
        it.val = nullptr;
217
0
        it.entry = nullptr;
218
0
        json_object_object_foreachC(poFields, it)
219
0
        {
220
0
            OGRFieldDefn fldDefn(it.key, OFTString);
221
0
            poDefn->AddFieldDefn(&fldDefn);
222
0
        }
223
0
    }
224
0
    else
225
0
    {
226
        // Guess the fields' schema from the content of the features' "attributes"
227
        // element
228
0
        json_object *poObjFeatures =
229
0
            OGRGeoJSONFindMemberByName(poGJObject_, "features");
230
0
        if (poObjFeatures &&
231
0
            json_type_array == json_object_get_type(poObjFeatures))
232
0
        {
233
0
            gdal::DirectedAcyclicGraph<int, std::string> dag;
234
0
            std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn{};
235
0
            std::map<std::string, int> oMapFieldNameToIdx{};
236
0
            std::vector<int> anCurFieldIndices;
237
0
            std::set<int> aoSetUndeterminedTypeFields;
238
239
0
            const auto nFeatures = json_object_array_length(poObjFeatures);
240
0
            for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
241
0
            {
242
0
                json_object *poObjFeature =
243
0
                    json_object_array_get_idx(poObjFeatures, i);
244
0
                if (poObjFeature != nullptr &&
245
0
                    json_object_get_type(poObjFeature) == json_type_object)
246
0
                {
247
0
                    int nPrevFieldIdx = -1;
248
249
0
                    json_object *poObjProps =
250
0
                        OGRGeoJSONFindMemberByName(poObjFeature, "attributes");
251
0
                    if (nullptr != poObjProps &&
252
0
                        json_object_get_type(poObjProps) == json_type_object)
253
0
                    {
254
0
                        json_object_iter it;
255
0
                        it.key = nullptr;
256
0
                        it.val = nullptr;
257
0
                        it.entry = nullptr;
258
0
                        json_object_object_foreachC(poObjProps, it)
259
0
                        {
260
0
                            anCurFieldIndices.clear();
261
0
                            OGRGeoJSONReaderAddOrUpdateField(
262
0
                                anCurFieldIndices, oMapFieldNameToIdx,
263
0
                                apoFieldDefn, it.key, it.val,
264
0
                                /*bFlattenNestedAttributes = */ true,
265
0
                                /* chNestedAttributeSeparator = */ '.',
266
0
                                /* bArrayAsString =*/false,
267
0
                                /* bDateAsString = */ false,
268
0
                                aoSetUndeterminedTypeFields);
269
0
                            for (int idx : anCurFieldIndices)
270
0
                            {
271
0
                                dag.addNode(idx,
272
0
                                            apoFieldDefn[idx]->GetNameRef());
273
0
                                if (nPrevFieldIdx != -1)
274
0
                                {
275
0
                                    dag.addEdge(nPrevFieldIdx, idx);
276
0
                                }
277
0
                                nPrevFieldIdx = idx;
278
0
                            }
279
0
                        }
280
0
                    }
281
0
                }
282
0
            }
283
284
0
            const auto sortedFields = dag.getTopologicalOrdering();
285
0
            CPLAssert(sortedFields.size() == apoFieldDefn.size());
286
0
            for (int idx : sortedFields)
287
0
            {
288
                // cppcheck-suppress containerOutOfBounds
289
0
                poDefn->AddFieldDefn(apoFieldDefn[idx].get());
290
0
            }
291
0
        }
292
0
    }
293
294
0
    return bSuccess;
295
0
}
296
297
/************************************************************************/
298
/*                             ParseField()                             */
299
/************************************************************************/
300
301
static const std::map<std::string, std::pair<OGRFieldType, OGRFieldSubType>>
302
    goMapEsriTypeToOGR = {
303
        {"esriFieldTypeString", {OFTString, OFSTNone}},
304
        {"esriFieldTypeSingle", {OFTReal, OFSTFloat32}},
305
        {"esriFieldTypeDouble", {OFTReal, OFSTNone}},
306
        {"esriFieldTypeSmallInteger", {OFTInteger, OFSTInt16}},
307
        {"esriFieldTypeInteger", {OFTInteger, OFSTNone}},
308
        {"esriFieldTypeDate", {OFTDateTime, OFSTNone}},
309
        {"esriFieldTypeDateOnly", {OFTDate, OFSTNone}},
310
        {"esriFieldTypeTimeOnly", {OFTTime, OFSTNone}},
311
        {"esriFieldTypeBigInteger", {OFTInteger64, OFSTNone}},
312
        {"esriFieldTypeGUID", {OFTString, OFSTUUID}},
313
        {"esriFieldTypeGlobalID", {OFTString, OFSTUUID}},
314
};
315
316
bool OGRESRIJSONReader::ParseField(json_object *poObj)
317
0
{
318
0
    OGRFeatureDefn *poDefn = poLayer_->GetLayerDefn();
319
0
    CPLAssert(nullptr != poDefn);
320
321
0
    bool bSuccess = false;
322
323
    /* -------------------------------------------------------------------- */
324
    /*      Read collection of properties.                                  */
325
    /* -------------------------------------------------------------------- */
326
0
    json_object *poObjName = OGRGeoJSONFindMemberByName(poObj, "name");
327
0
    json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type");
328
0
    if (nullptr != poObjName && nullptr != poObjType)
329
0
    {
330
0
        OGRFieldType eFieldType = OFTString;
331
0
        OGRFieldSubType eFieldSubType = OFSTNone;
332
0
        const char *pszObjName = json_object_get_string(poObjName);
333
0
        const char *pszObjType = json_object_get_string(poObjType);
334
0
        if (strcmp(pszObjType, "esriFieldTypeOID") == 0)
335
0
        {
336
0
            eFieldType = OFTInteger;
337
0
            poLayer_->SetFIDColumn(pszObjName);
338
0
        }
339
0
        else
340
0
        {
341
0
            const auto it = goMapEsriTypeToOGR.find(pszObjType);
342
0
            if (it != goMapEsriTypeToOGR.end())
343
0
            {
344
0
                eFieldType = it->second.first;
345
0
                eFieldSubType = it->second.second;
346
0
            }
347
0
            else
348
0
            {
349
0
                CPLDebug("ESRIJSON",
350
0
                         "Unhandled fields[\"%s\"].type = %s. "
351
0
                         "Processing it as a String",
352
0
                         pszObjName, pszObjType);
353
0
            }
354
0
        }
355
0
        OGRFieldDefn fldDefn(pszObjName, eFieldType);
356
0
        fldDefn.SetSubType(eFieldSubType);
357
358
0
        if (eFieldType != OFTDateTime)
359
0
        {
360
0
            json_object *const poObjLength =
361
0
                OGRGeoJSONFindMemberByName(poObj, "length");
362
0
            if (poObjLength != nullptr &&
363
0
                json_object_get_type(poObjLength) == json_type_int)
364
0
            {
365
0
                const int nWidth = json_object_get_int(poObjLength);
366
                // A dummy width of 2147483647 seems to indicate no known field with
367
                // which in the OGR world is better modelled as 0 field width.
368
                // (#6529)
369
0
                if (nWidth != INT_MAX)
370
0
                    fldDefn.SetWidth(nWidth);
371
0
            }
372
0
        }
373
374
0
        json_object *poObjAlias = OGRGeoJSONFindMemberByName(poObj, "alias");
375
0
        if (poObjAlias && json_object_get_type(poObjAlias) == json_type_string)
376
0
        {
377
0
            const char *pszAlias = json_object_get_string(poObjAlias);
378
0
            if (strcmp(pszObjName, pszAlias) != 0)
379
0
                fldDefn.SetAlternativeName(pszAlias);
380
0
        }
381
382
0
        poDefn->AddFieldDefn(&fldDefn);
383
384
0
        bSuccess = true;
385
0
    }
386
0
    return bSuccess;
387
0
}
388
389
/************************************************************************/
390
/*                           AddFeature                                 */
391
/************************************************************************/
392
393
bool OGRESRIJSONReader::AddFeature(OGRFeature *poFeature)
394
0
{
395
0
    if (nullptr == poFeature)
396
0
        return false;
397
398
0
    poLayer_->AddFeature(poFeature);
399
0
    delete poFeature;
400
401
0
    return true;
402
0
}
403
404
/************************************************************************/
405
/*                           EsriDateToOGRDate()                        */
406
/************************************************************************/
407
408
static void EsriDateToOGRDate(int64_t nVal, OGRField *psField)
409
0
{
410
0
    const auto nSeconds = nVal / 1000;
411
0
    const auto nMillisec = static_cast<int>(nVal % 1000);
412
413
0
    struct tm brokendowntime;
414
0
    CPLUnixTimeToYMDHMS(nSeconds, &brokendowntime);
415
416
0
    psField->Date.Year = static_cast<GInt16>(brokendowntime.tm_year + 1900);
417
0
    psField->Date.Month = static_cast<GByte>(brokendowntime.tm_mon + 1);
418
0
    psField->Date.Day = static_cast<GByte>(brokendowntime.tm_mday);
419
0
    psField->Date.Hour = static_cast<GByte>(brokendowntime.tm_hour);
420
0
    psField->Date.Minute = static_cast<GByte>(brokendowntime.tm_min);
421
0
    psField->Date.Second =
422
0
        static_cast<float>(brokendowntime.tm_sec + nMillisec / 1000.0);
423
0
    psField->Date.TZFlag = 100;
424
0
    psField->Date.Reserved = 0;
425
0
}
426
427
/************************************************************************/
428
/*                           ReadFeature()                              */
429
/************************************************************************/
430
431
OGRFeature *OGRESRIJSONReader::ReadFeature(json_object *poObj)
432
0
{
433
0
    CPLAssert(nullptr != poObj);
434
0
    CPLAssert(nullptr != poLayer_);
435
436
0
    OGRFeature *poFeature = new OGRFeature(poLayer_->GetLayerDefn());
437
438
    /* -------------------------------------------------------------------- */
439
    /*      Translate ESRIJSON "attributes" object to feature attributes.   */
440
    /* -------------------------------------------------------------------- */
441
0
    CPLAssert(nullptr != poFeature);
442
443
0
    json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "attributes");
444
0
    if (nullptr != poObjProps &&
445
0
        json_object_get_type(poObjProps) == json_type_object)
446
0
    {
447
0
        const OGRFieldDefn *poFieldDefn = nullptr;
448
0
        json_object_iter it;
449
0
        it.key = nullptr;
450
0
        it.val = nullptr;
451
0
        it.entry = nullptr;
452
0
        json_object_object_foreachC(poObjProps, it)
453
0
        {
454
0
            const int nField = poFeature->GetFieldIndex(it.key);
455
0
            if (nField >= 0)
456
0
            {
457
0
                poFieldDefn = poFeature->GetFieldDefnRef(nField);
458
0
                if (poFieldDefn && it.val != nullptr)
459
0
                {
460
0
                    if (EQUAL(it.key, poLayer_->GetFIDColumn()))
461
0
                        poFeature->SetFID(json_object_get_int(it.val));
462
0
                    switch (poLayer_->GetLayerDefn()
463
0
                                ->GetFieldDefn(nField)
464
0
                                ->GetType())
465
0
                    {
466
0
                        case OFTInteger:
467
0
                        {
468
0
                            poFeature->SetField(nField,
469
0
                                                json_object_get_int(it.val));
470
0
                            break;
471
0
                        }
472
0
                        case OFTReal:
473
0
                        {
474
0
                            poFeature->SetField(nField,
475
0
                                                json_object_get_double(it.val));
476
0
                            break;
477
0
                        }
478
0
                        case OFTDateTime:
479
0
                        {
480
0
                            const auto nVal = json_object_get_int64(it.val);
481
0
                            EsriDateToOGRDate(
482
0
                                nVal, poFeature->GetRawFieldRef(nField));
483
0
                            break;
484
0
                        }
485
0
                        default:
486
0
                        {
487
0
                            poFeature->SetField(nField,
488
0
                                                json_object_get_string(it.val));
489
0
                            break;
490
0
                        }
491
0
                    }
492
0
                }
493
0
            }
494
0
        }
495
0
    }
496
497
0
    const OGRwkbGeometryType eType = poLayer_->GetGeomType();
498
0
    if (eType == wkbNone)
499
0
        return poFeature;
500
501
    /* -------------------------------------------------------------------- */
502
    /*      Translate geometry sub-object of ESRIJSON Feature.               */
503
    /* -------------------------------------------------------------------- */
504
0
    json_object *poObjGeom = nullptr;
505
0
    json_object *poTmp = poObj;
506
0
    json_object_iter it;
507
0
    it.key = nullptr;
508
0
    it.val = nullptr;
509
0
    it.entry = nullptr;
510
0
    json_object_object_foreachC(poTmp, it)
511
0
    {
512
0
        if (EQUAL(it.key, "geometry"))
513
0
        {
514
0
            if (it.val != nullptr)
515
0
                poObjGeom = it.val;
516
            // We're done.  They had 'geometry':null.
517
0
            else
518
0
                return poFeature;
519
0
        }
520
0
    }
521
522
0
    if (nullptr != poObjGeom)
523
0
    {
524
0
        OGRGeometry *poGeometry = OGRESRIJSONReadGeometry(poObjGeom);
525
0
        if (nullptr != poGeometry)
526
0
        {
527
0
            poFeature->SetGeometryDirectly(poGeometry);
528
0
        }
529
0
    }
530
531
0
    return poFeature;
532
0
}
533
534
/************************************************************************/
535
/*                           ReadFeatureCollection()                    */
536
/************************************************************************/
537
538
OGRGeoJSONLayer *OGRESRIJSONReader::ReadFeatureCollection(json_object *poObj)
539
0
{
540
0
    CPLAssert(nullptr != poLayer_);
541
542
0
    json_object *poObjFeatures = OGRGeoJSONFindMemberByName(poObj, "features");
543
0
    if (nullptr == poObjFeatures)
544
0
    {
545
0
        CPLError(CE_Failure, CPLE_AppDefined,
546
0
                 "Invalid FeatureCollection object. "
547
0
                 "Missing \'features\' member.");
548
0
        return nullptr;
549
0
    }
550
551
0
    if (json_type_array == json_object_get_type(poObjFeatures))
552
0
    {
553
0
        const auto nFeatures = json_object_array_length(poObjFeatures);
554
0
        for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
555
0
        {
556
0
            json_object *poObjFeature =
557
0
                json_object_array_get_idx(poObjFeatures, i);
558
0
            if (poObjFeature != nullptr &&
559
0
                json_object_get_type(poObjFeature) == json_type_object)
560
0
            {
561
0
                OGRFeature *poFeature =
562
0
                    OGRESRIJSONReader::ReadFeature(poObjFeature);
563
0
                AddFeature(poFeature);
564
0
            }
565
0
        }
566
0
    }
567
568
    // We're returning class member to follow the same pattern of
569
    // Read* functions call convention.
570
0
    CPLAssert(nullptr != poLayer_);
571
0
    return poLayer_;
572
0
}