@@ -229,6 +229,12 @@ module json_value_module
229229 ! ! [Note: `no_whitespace` will
230230 ! ! override this option if necessary]
231231
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+
232238 contains
233239
234240 private
@@ -781,7 +787,8 @@ function initialize_json_core(verbose,compact_reals,&
781787 comment_char ,&
782788 path_mode ,&
783789 path_separator ,&
784- compress_vectors ) result(json_core_object)
790+ compress_vectors ,&
791+ allow_duplicate_keys ) result(json_core_object)
785792
786793 implicit none
787794
@@ -798,7 +805,8 @@ function initialize_json_core(verbose,compact_reals,&
798805 comment_char,&
799806 path_mode,&
800807 path_separator,&
801- compress_vectors)
808+ compress_vectors,&
809+ allow_duplicate_keys)
802810
803811 end function initialize_json_core
804812! *****************************************************************************************
@@ -832,7 +840,8 @@ subroutine json_initialize(me,verbose,compact_reals,&
832840 comment_char ,&
833841 path_mode ,&
834842 path_separator ,&
835- compress_vectors )
843+ compress_vectors ,&
844+ allow_duplicate_keys )
836845
837846 implicit none
838847
@@ -904,6 +913,11 @@ subroutine json_initialize(me,verbose,compact_reals,&
904913 me% compress_vectors = compress_vectors
905914 end if
906915
916+ ! checking for duplicate keys:
917+ if (present (allow_duplicate_keys)) then
918+ me% allow_duplicate_keys = allow_duplicate_keys
919+ end if
920+
907921 ! Set the format for real numbers:
908922 ! [if not changing it, then it remains the same]
909923
@@ -2372,21 +2386,77 @@ end function json_value_is_child_of
23722386!
23732387! It recursively traverses the entire structure and checks every element.
23742388!
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+ !
23752394! @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.
23762397
23772398 subroutine json_value_validate (json ,p ,is_valid ,error_msg )
23782399
23792400 implicit none
23802401
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.
23842405 character (kind= CK,len= :),allocatable ,intent (out ) :: error_msg ! ! if not valid, this will contain
23852406 ! ! a description of the problem
23862407
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+
23872415 if (associated (p)) then
2416+
23882417 is_valid = .true.
23892418 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+
23902460 else
23912461 error_msg = ' The pointer is not associated'
23922462 is_valid = .false.
@@ -7726,6 +7796,8 @@ subroutine json_parse_file(json, file, p, unit)
77267796 integer (IK) :: iunit ! ! file unit actually used
77277797 integer (IK) :: istat ! ! iostat flag
77287798 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
77297801
77307802 ! clear any exceptions and initialize:
77317803 call json% initialize()
@@ -7782,11 +7854,25 @@ subroutine json_parse_file(json, file, p, unit)
77827854
77837855 ! parse as a value
77847856 call json% parse_value(unit= iunit, str= CK_' ' , value= p)
7785- if (json% exception_thrown) call json% annotate_invalid_json(iunit,CK_' ' )
77867857
77877858 ! close the file if necessary
77887859 close (unit= iunit, iostat= istat)
77897860
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+
77907876 else
77917877
77927878 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)
78147900
78157901 integer (IK),parameter :: iunit = 0 ! ! indicates that json data will be read from buffer
78167902
7903+ logical (LK) :: has_duplicate ! ! if checking for duplicate keys
7904+ character (kind= CDK,len= :),allocatable :: path ! ! path to any duplicate key
7905+
78177906 ! clear any exceptions and initialize:
78187907 call json% initialize()
78197908
@@ -7827,7 +7916,19 @@ subroutine json_parse_string(json, p, str)
78277916 ! parse as a value
78287917 call json% parse_value(unit= iunit, str= str, value= p)
78297918
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
78317932
78327933 end subroutine json_parse_string
78337934! *****************************************************************************************
0 commit comments