@@ -229,6 +229,12 @@ module json_value_module
229
229
! ! [Note: `no_whitespace` will
230
230
! ! override this option if necessary]
231
231
232
+ logical (LK) :: allow_duplicate_keys = .true. ! ! If False, then after parsing, if any
233
+ ! ! duplicate keys are found, an error is
234
+ ! ! thrown. A call to [[json_value_validate]]
235
+ ! ! will also check for duplicates. If True
236
+ ! ! [default] then no special checks are done
237
+
232
238
contains
233
239
234
240
private
@@ -781,7 +787,8 @@ function initialize_json_core(verbose,compact_reals,&
781
787
comment_char ,&
782
788
path_mode ,&
783
789
path_separator ,&
784
- compress_vectors ) result(json_core_object)
790
+ compress_vectors ,&
791
+ allow_duplicate_keys ) result(json_core_object)
785
792
786
793
implicit none
787
794
@@ -798,7 +805,8 @@ function initialize_json_core(verbose,compact_reals,&
798
805
comment_char,&
799
806
path_mode,&
800
807
path_separator,&
801
- compress_vectors)
808
+ compress_vectors,&
809
+ allow_duplicate_keys)
802
810
803
811
end function initialize_json_core
804
812
! *****************************************************************************************
@@ -832,7 +840,8 @@ subroutine json_initialize(me,verbose,compact_reals,&
832
840
comment_char ,&
833
841
path_mode ,&
834
842
path_separator ,&
835
- compress_vectors )
843
+ compress_vectors ,&
844
+ allow_duplicate_keys )
836
845
837
846
implicit none
838
847
@@ -904,6 +913,11 @@ subroutine json_initialize(me,verbose,compact_reals,&
904
913
me% compress_vectors = compress_vectors
905
914
end if
906
915
916
+ ! checking for duplicate keys:
917
+ if (present (allow_duplicate_keys)) then
918
+ me% allow_duplicate_keys = allow_duplicate_keys
919
+ end if
920
+
907
921
! Set the format for real numbers:
908
922
! [if not changing it, then it remains the same]
909
923
@@ -2372,21 +2386,77 @@ end function json_value_is_child_of
2372
2386
!
2373
2387
! It recursively traverses the entire structure and checks every element.
2374
2388
!
2389
+ ! ### History
2390
+ ! * Jacob Williams, 8/26/2017 : added duplicate key check.
2391
+ !
2392
+ ! @note It will return on the first error it encounters.
2393
+ !
2375
2394
! @note This routine does not check or throw any exceptions.
2395
+ ! If `json` is currently in a state of exception, it will
2396
+ ! remain so after calling this routine.
2376
2397
2377
2398
subroutine json_value_validate (json ,p ,is_valid ,error_msg )
2378
2399
2379
2400
implicit none
2380
2401
2381
- class(json_core),intent (inout ) :: json
2382
- type (json_value),pointer ,intent (in ) :: p
2383
- logical (LK),intent (out ) :: is_valid ! ! True if the structure is valid.
2402
+ class(json_core),intent (inout ) :: json
2403
+ type (json_value),pointer ,intent (in ) :: p
2404
+ logical (LK),intent (out ) :: is_valid ! ! True if the structure is valid.
2384
2405
character (kind= CK,len= :),allocatable ,intent (out ) :: error_msg ! ! if not valid, this will contain
2385
2406
! ! a description of the problem
2386
2407
2408
+ logical (LK) :: has_duplicate ! ! to check for duplicate keys
2409
+ character (kind= CK,len= :),allocatable :: path ! ! path to duplicate key
2410
+ logical (LK) :: status_ok ! ! to check for existing exception
2411
+ logical (LK) :: status_ok2 ! ! to check for a new exception
2412
+ character (kind= CK,len= :),allocatable :: exception_msg ! ! error message for an existing exception
2413
+ character (kind= CK,len= :),allocatable :: exception_msg2 ! ! error message for a new exception
2414
+
2387
2415
if (associated (p)) then
2416
+
2388
2417
is_valid = .true.
2389
2418
call check_if_valid(p,require_parent= associated (p% parent))
2419
+
2420
+ if (is_valid .and. .not. json% allow_duplicate_keys) then
2421
+ ! if no errors so far, also check the
2422
+ ! entire structure for duplicate keys:
2423
+
2424
+ ! note: check_for_duplicate_keys does call routines
2425
+ ! that check and throw exceptions, so let's clear any
2426
+ ! first. (save message for later)
2427
+ call json% check_for_errors(status_ok, exception_msg)
2428
+ call json% clear_exceptions()
2429
+
2430
+ call json% check_for_duplicate_keys(p,has_duplicate,path= path)
2431
+ if (json% failed()) then
2432
+ ! if an exception was thrown during this call,
2433
+ ! then clear it but make that the error message
2434
+ ! returned by this routine. Normally this should
2435
+ ! never actually occur since we have already
2436
+ ! validated the structure.
2437
+ call json% check_for_errors(is_valid, exception_msg2)
2438
+ error_msg = exception_msg2
2439
+ call json% clear_exceptions()
2440
+ is_valid = .false.
2441
+ else
2442
+ if (has_duplicate) then
2443
+ error_msg = ' duplicate key found: ' // path
2444
+ is_valid = .false.
2445
+ end if
2446
+ end if
2447
+
2448
+ if (.not. status_ok) then
2449
+ ! restore any existing exception if necessary
2450
+ call json% throw_exception(exception_msg)
2451
+ end if
2452
+
2453
+ ! cleanup:
2454
+ if (allocated (path)) deallocate (path)
2455
+ if (allocated (exception_msg)) deallocate (exception_msg)
2456
+ if (allocated (exception_msg2)) deallocate (exception_msg2)
2457
+
2458
+ end if
2459
+
2390
2460
else
2391
2461
error_msg = ' The pointer is not associated'
2392
2462
is_valid = .false.
@@ -7726,6 +7796,8 @@ subroutine json_parse_file(json, file, p, unit)
7726
7796
integer (IK) :: iunit ! ! file unit actually used
7727
7797
integer (IK) :: istat ! ! iostat flag
7728
7798
logical (LK) :: is_open ! ! if the file is already open
7799
+ logical (LK) :: has_duplicate ! ! if checking for duplicate keys
7800
+ character (kind= CDK,len= :),allocatable :: path ! ! path to any duplicate key
7729
7801
7730
7802
! clear any exceptions and initialize:
7731
7803
call json% initialize()
@@ -7782,11 +7854,25 @@ subroutine json_parse_file(json, file, p, unit)
7782
7854
7783
7855
! parse as a value
7784
7856
call json% parse_value(unit= iunit, str= CK_' ' , value= p)
7785
- if (json% exception_thrown) call json% annotate_invalid_json(iunit,CK_' ' )
7786
7857
7787
7858
! close the file if necessary
7788
7859
close (unit= iunit, iostat= istat)
7789
7860
7861
+ ! check for errors:
7862
+ if (json% exception_thrown) then
7863
+ call json% annotate_invalid_json(iunit,CK_' ' )
7864
+ else
7865
+ if (.not. json% allow_duplicate_keys) then
7866
+ call json% check_for_duplicate_keys(p,has_duplicate,path= path)
7867
+ if (.not. json% exception_thrown) then
7868
+ if (has_duplicate) then
7869
+ call json% throw_exception(' Error in json_parse_file: ' // &
7870
+ ' Duplicate key found: ' // path)
7871
+ end if
7872
+ end if
7873
+ end if
7874
+ end if
7875
+
7790
7876
else
7791
7877
7792
7878
call json% throw_exception(' Error in json_parse_file: Error opening file: ' // trim (file))
@@ -7814,6 +7900,9 @@ subroutine json_parse_string(json, p, str)
7814
7900
7815
7901
integer (IK),parameter :: iunit = 0 ! ! indicates that json data will be read from buffer
7816
7902
7903
+ logical (LK) :: has_duplicate ! ! if checking for duplicate keys
7904
+ character (kind= CDK,len= :),allocatable :: path ! ! path to any duplicate key
7905
+
7817
7906
! clear any exceptions and initialize:
7818
7907
call json% initialize()
7819
7908
@@ -7827,7 +7916,19 @@ subroutine json_parse_string(json, p, str)
7827
7916
! parse as a value
7828
7917
call json% parse_value(unit= iunit, str= str, value= p)
7829
7918
7830
- if (json% exception_thrown) call json% annotate_invalid_json(iunit,str)
7919
+ if (json% exception_thrown) then
7920
+ call json% annotate_invalid_json(iunit,str)
7921
+ else
7922
+ if (.not. json% allow_duplicate_keys) then
7923
+ call json% check_for_duplicate_keys(p,has_duplicate,path= path)
7924
+ if (.not. json% exception_thrown) then
7925
+ if (has_duplicate) then
7926
+ call json% throw_exception(' Error in json_parse_string: ' // &
7927
+ ' Duplicate key found: ' // path)
7928
+ end if
7929
+ end if
7930
+ end if
7931
+ end if
7831
7932
7832
7933
end subroutine json_parse_string
7833
7934
! *****************************************************************************************
0 commit comments