@@ -671,9 +671,16 @@ module json_value_module
671
671
! ! list is valid (i.e., is properly
672
672
! ! constructed). This may be useful
673
673
! ! if it has been constructed externally.
674
+ procedure ,public :: check_for_duplicate_keys &
675
+ = > json_check_all_for_duplicate_keys ! ! Check entire JSON structure
676
+ ! ! for duplicate keys (recursively)
677
+ procedure ,public :: check_children_for_duplicate_keys &
678
+ = > json_check_children_for_duplicate_keys ! ! Check a `json_value` object's
679
+ ! ! children for duplicate keys
674
680
675
681
! other private routines:
676
682
procedure :: name_equal
683
+ procedure :: name_strings_equal
677
684
procedure :: json_value_print
678
685
procedure :: string_to_int
679
686
procedure :: string_to_dble
@@ -963,10 +970,13 @@ end subroutine json_initialize
963
970
964
971
! *****************************************************************************************
965
972
! > author: Jacob Williams
966
- ! date: 4/30/2016
967
973
!
968
974
! Returns true if `name` is equal to `p%name`, using the specified
969
975
! settings for case sensitivity and trailing whitespace.
976
+ !
977
+ ! ### History
978
+ ! * 4/30/2016 : original version
979
+ ! * 8/25/2017 : now just a wrapper for [[name_strings_equal]]
970
980
971
981
function name_equal (json ,p ,name ) result(is_equal)
972
982
@@ -975,29 +985,51 @@ function name_equal(json,p,name) result(is_equal)
975
985
class(json_core),intent (inout ) :: json
976
986
type (json_value),intent (in ) :: p ! ! the json object
977
987
character (kind= CK,len=* ),intent (in ) :: name ! ! the name to check for
978
- logical (LK) :: is_equal ! ! true if the string are lexically equal
988
+ logical (LK) :: is_equal ! ! true if the string are
989
+ ! ! lexically equal
979
990
980
991
if (allocated (p% name)) then
992
+ ! call the low-level routines for the name strings:
993
+ is_equal = json% name_strings_equal(p% name,name)
994
+ else
995
+ is_equal = name == CK_' ' ! check a blank name
996
+ end if
981
997
982
- ! must be the same length if we are treating
983
- ! trailing spaces as significant, so do a
984
- ! quick test of this first:
985
- if (json% trailing_spaces_significant) then
986
- is_equal = len (p% name) == len (name)
987
- if (.not. is_equal) return
988
- end if
998
+ end function name_equal
999
+ ! *****************************************************************************************
989
1000
990
- if (json% case_sensitive_keys) then
991
- is_equal = p% name == name
992
- else
993
- is_equal = lowercase_string(p% name) == lowercase_string(name)
994
- end if
1001
+ ! *****************************************************************************************
1002
+ ! > author: Jacob Williams
1003
+ ! date: 8/25/2017
1004
+ !
1005
+ ! Returns true if the name strings `name1` is equal to `name2`, using
1006
+ ! the specified settings for case sensitivity and trailing whitespace.
1007
+
1008
+ function name_strings_equal (json ,name1 ,name2 ) result(is_equal)
1009
+
1010
+ implicit none
1011
+
1012
+ class(json_core),intent (inout ) :: json
1013
+ character (kind= CK,len=* ),intent (in ) :: name1 ! ! the name to check
1014
+ character (kind= CK,len=* ),intent (in ) :: name2 ! ! the name to check
1015
+ logical (LK) :: is_equal ! ! true if the string are
1016
+ ! ! lexically equal
1017
+
1018
+ ! must be the same length if we are treating
1019
+ ! trailing spaces as significant, so do a
1020
+ ! quick test of this first:
1021
+ if (json% trailing_spaces_significant) then
1022
+ is_equal = len (name1) == len (name2)
1023
+ if (.not. is_equal) return
1024
+ end if
995
1025
1026
+ if (json% case_sensitive_keys) then
1027
+ is_equal = name1 == name2
996
1028
else
997
- is_equal = name == CK_ ' ' ! check a blank name
1029
+ is_equal = lowercase_string(name1) == lowercase_string(name2)
998
1030
end if
999
1031
1000
- end function name_equal
1032
+ end function name_strings_equal
1001
1033
! *****************************************************************************************
1002
1034
1003
1035
! *****************************************************************************************
@@ -4681,8 +4713,9 @@ subroutine json_value_get_child_by_name(json, p, name, child, found)
4681
4713
child = > p% children ! start with first one
4682
4714
do i= 1 , n_children
4683
4715
if (.not. associated (child)) then
4684
- call json% throw_exception(' Error in json_value_get_child_by_name: ' // &
4685
- ' Malformed JSON linked list' )
4716
+ call json% throw_exception(&
4717
+ ' Error in json_value_get_child_by_name: ' // &
4718
+ ' Malformed JSON linked list' )
4686
4719
exit
4687
4720
end if
4688
4721
if (allocated (child% name)) then
@@ -4698,14 +4731,16 @@ subroutine json_value_get_child_by_name(json, p, name, child, found)
4698
4731
4699
4732
if (error) then
4700
4733
! did not find anything:
4701
- call json% throw_exception(' Error in json_value_get_child_by_name: ' // &
4702
- ' child variable ' // trim (name)// ' was not found.' )
4734
+ call json% throw_exception(&
4735
+ ' Error in json_value_get_child_by_name: ' // &
4736
+ ' child variable ' // trim (name)// ' was not found.' )
4703
4737
nullify(child)
4704
4738
end if
4705
4739
4706
4740
else
4707
- call json% throw_exception(' Error in json_value_get_child_by_name: ' // &
4708
- ' pointer is not associated.' )
4741
+ call json% throw_exception(&
4742
+ ' Error in json_value_get_child_by_name: ' // &
4743
+ ' pointer is not associated.' )
4709
4744
end if
4710
4745
4711
4746
! found output:
@@ -4725,6 +4760,186 @@ subroutine json_value_get_child_by_name(json, p, name, child, found)
4725
4760
end subroutine json_value_get_child_by_name
4726
4761
! *****************************************************************************************
4727
4762
4763
+ ! *****************************************************************************************
4764
+ ! > author: Jacob Williams
4765
+ ! date: 8/25/2017
4766
+ !
4767
+ ! Checks a JSON object for duplicate child names.
4768
+ !
4769
+ ! It uses the specified settings for name matching (see [[name_strings_equal]]).
4770
+ !
4771
+ ! @note This will only check for one duplicate,
4772
+ ! it will return the first one that it finds.
4773
+
4774
+ subroutine json_check_children_for_duplicate_keys (json ,p ,has_duplicate ,name ,path )
4775
+
4776
+ implicit none
4777
+
4778
+ class(json_core),intent (inout ) :: json
4779
+ type (json_value),pointer ,intent (in ) :: p ! ! the object to search. If `p` is
4780
+ ! ! not a `json_object`, then `has_duplicate`
4781
+ ! ! will be false.
4782
+ logical (LK),intent (out ) :: has_duplicate ! ! true if there is at least
4783
+ ! ! two children have duplicate
4784
+ ! ! `name` values.
4785
+ character (kind= CK,len= :),allocatable ,intent (out ),optional :: name ! ! the duplicate name
4786
+ ! ! (unallocated if no
4787
+ ! ! duplicate was found)
4788
+ character (kind= CK,len= :),allocatable ,intent (out ),optional :: path ! ! the full path to the
4789
+ ! ! duplicate name
4790
+ ! ! (unallocated if no
4791
+ ! ! duplicate was found)
4792
+
4793
+ integer (IK) :: i ! ! counter
4794
+ integer (IK) :: j ! ! counter
4795
+ type (json_value),pointer :: child ! ! pointer to a child of `p`
4796
+ integer (IK) :: n_children ! ! number of children of `p`
4797
+ logical (LK) :: found ! ! flag for `get_child`
4798
+
4799
+ type :: alloc_str
4800
+ ! ! so we can have an array of allocatable strings
4801
+ character (kind= CK,len= :),allocatable :: str ! ! name string
4802
+ end type alloc_str
4803
+ type (alloc_str),dimension (:),allocatable :: names ! ! array of all the
4804
+ ! ! child name strings
4805
+
4806
+ ! initialize:
4807
+ has_duplicate = .false.
4808
+
4809
+ if (.not. json% exception_thrown) then
4810
+
4811
+ if (associated (p)) then
4812
+
4813
+ if (p% var_type== json_object) then
4814
+
4815
+ ! number of items to check:
4816
+ n_children = json% count (p)
4817
+ allocate (names(n_children))
4818
+
4819
+ ! first get a list of all the name keys:
4820
+ do i= 1 , n_children
4821
+ call json% get_child(p,i,child,found) ! get by index
4822
+ if (.not. found) then
4823
+ call json% throw_exception(&
4824
+ ' Error in json_check_children_for_duplicate_keys: ' // &
4825
+ ' Malformed JSON linked list' )
4826
+ exit
4827
+ end if
4828
+ if (allocated (child% name)) then
4829
+ names(i)% str = child% name
4830
+ else
4831
+ call json% throw_exception(&
4832
+ ' Error in json_check_children_for_duplicate_keys: ' // &
4833
+ ' Object child name is not allocated' )
4834
+ exit
4835
+ end if
4836
+ end do
4837
+
4838
+ if (.not. json% exception_thrown) then
4839
+ ! now check the list for duplicates:
4840
+ main: do i= 1 ,n_children
4841
+ do j= 1 ,i-1
4842
+ if (json% name_strings_equal(names(i)% str,names(j)% str)) then
4843
+ has_duplicate = .true.
4844
+ if (present (name)) then
4845
+ name = names(i)% str
4846
+ end if
4847
+ if (present (path)) then
4848
+ call json% get_child(p,names(i)% str,child,found) ! get by name
4849
+ if (found) then
4850
+ call json% get_path(child,path,found)
4851
+ if (.not. found) then
4852
+ ! should never happen since we know it is there
4853
+ call json% throw_exception(&
4854
+ ' Error in json_check_children_for_duplicate_keys: ' // &
4855
+ ' Could not get path' )
4856
+ end if
4857
+ else
4858
+ ! should never happen since we know it is there
4859
+ call json% throw_exception(&
4860
+ ' Error in json_check_children_for_duplicate_keys: ' // &
4861
+ ' Could not get child: ' // trim (names(i)% str))
4862
+ end if
4863
+ end if
4864
+ exit main
4865
+ end if
4866
+ end do
4867
+ end do main
4868
+ end if
4869
+
4870
+ ! cleanup
4871
+ do i= 1 ,n_children
4872
+ if (allocated (names(i)% str)) deallocate (names(i)% str)
4873
+ end do
4874
+ if (allocated (names)) deallocate (names)
4875
+
4876
+ end if
4877
+
4878
+ end if
4879
+
4880
+ end if
4881
+
4882
+ end subroutine json_check_children_for_duplicate_keys
4883
+ ! *****************************************************************************************
4884
+
4885
+ ! *****************************************************************************************
4886
+ ! > author: Jacob Williams
4887
+ ! date: 8/25/2017
4888
+ !
4889
+ ! Checks a JSON structure for duplicate child names.
4890
+ ! This one recursively traverses the entire structure
4891
+ ! (calling [[json_check_children_for_duplicate_keys]]
4892
+ ! recursively for each element).
4893
+ !
4894
+ ! @note This will only check for one duplicate,
4895
+ ! it will return the first one that it finds.
4896
+
4897
+ subroutine json_check_all_for_duplicate_keys (json ,p ,has_duplicate ,name ,path )
4898
+
4899
+ implicit none
4900
+
4901
+ class(json_core),intent (inout ) :: json
4902
+ type (json_value),pointer ,intent (in ) :: p ! ! the object to search. If `p` is
4903
+ ! ! not a `json_object`, then `has_duplicate`
4904
+ ! ! will be false.
4905
+ logical (LK),intent (out ) :: has_duplicate ! ! true if there is at least
4906
+ ! ! one duplicate `name` key anywhere
4907
+ ! ! in the structure.
4908
+ character (kind= CK,len= :),allocatable ,intent (out ),optional :: name ! ! the duplicate name
4909
+ ! ! (unallocated if no
4910
+ ! ! duplicates were found)
4911
+ character (kind= CK,len= :),allocatable ,intent (out ),optional :: path ! ! the full path to the
4912
+ ! ! duplicate name
4913
+ ! ! (unallocated if no
4914
+ ! ! duplicate was found)
4915
+
4916
+ has_duplicate = .false.
4917
+ if (.not. json% exception_thrown) then
4918
+ call json% traverse(p,duplicate_key_func)
4919
+ end if
4920
+
4921
+ contains
4922
+
4923
+ subroutine duplicate_key_func (json ,p ,finished )
4924
+
4925
+ ! ! Callback function to check each element
4926
+ ! ! for duplicate child names.
4927
+
4928
+ implicit none
4929
+
4930
+ class(json_core),intent (inout ) :: json
4931
+ type (json_value),pointer ,intent (in ) :: p
4932
+ logical (LK),intent (out ) :: finished
4933
+
4934
+ call json% check_children_for_duplicate_keys(p,has_duplicate,name,path)
4935
+
4936
+ finished = has_duplicate .or. json% exception_thrown
4937
+
4938
+ end subroutine duplicate_key_func
4939
+
4940
+ end subroutine json_check_all_for_duplicate_keys
4941
+ ! *****************************************************************************************
4942
+
4728
4943
! *****************************************************************************************
4729
4944
! >
4730
4945
! Alternate version of [[json_value_get_child_by_name]] where `name` is kind=CDK.
0 commit comments