diff --git a/json-fortran.fobis b/json-fortran.fobis index 019e74fda..7110cd4ca 100644 --- a/json-fortran.fobis +++ b/json-fortran.fobis @@ -149,6 +149,8 @@ rule_1 = (cd bin GLOBIGNORE='*.*' ls jf_test_* | sed 's/^\([^0-9]*\)\([0-9]*\)/\1 \2/' | sort -k2,2n | tr -d ' ' | while read TEST; do + echo "" + echo "======================================================" echo "" echo "Running ${TEST}" "./${TEST}" diff --git a/src/json_value_module.F90 b/src/json_value_module.F90 index b43555249..3b576feca 100644 --- a/src/json_value_module.F90 +++ b/src/json_value_module.F90 @@ -68,6 +68,49 @@ module json_value_module #endif !********************************************************* + type,abstract :: json_data + !! to hold the various types of JSON data + private + end type json_data + + type,extends(json_data),abstract :: json_data_with_children + !! a json data type that can have children + !! (an object or array) + integer(IK) :: n_children = 0 !! number of children + end type json_data_with_children + + type,extends(json_data) :: json_real_type + !! a `json_real` variable + real(RK) :: value = 0.0_RK + end type json_real_type + + type,extends(json_data) :: json_logical_type + !! a `json_logical` variable + logical(LK) :: value = .false. + end type json_logical_type + + type,extends(json_data) :: json_string_type + !! a `json_string` variable + character(kind=CK,len=:),allocatable :: value + end type json_string_type + + type,extends(json_data) :: json_integer_type + !! a `json_integer` variable + integer(IK) :: value = 0.0_IK + end type json_integer_type + + type,extends(json_data) :: json_null_type + !! a `json_null` variable + end type json_null_type + + type,extends(json_data_with_children) :: json_object_type + !! a `json_object` variable + end type json_object_type + + type,extends(json_data_with_children) :: json_array_type + !! a `json_array` variable + end type json_array_type + !********************************************************* !> ! Type used to construct the linked-list JSON structure. @@ -103,14 +146,8 @@ module json_value_module ! !@warning Pointers of this type should only be allocated ! using the methods from [[json_core(type)]]. - type,public :: json_value - !force the constituents to be stored contiguously - ![note: on Intel, the order of the variables below - ! is significant to avoid the misaligned field warnings] - sequence - private !for the linked list: @@ -122,15 +159,9 @@ module json_value_module character(kind=CK,len=:),allocatable :: name !! variable name (unescaped) - real(RK),allocatable :: dbl_value !! real data for this variable - logical(LK),allocatable :: log_value !! logical data for this variable - character(kind=CK,len=:),allocatable :: str_value !! string data for this variable - !! (unescaped) - integer(IK),allocatable :: int_value !! integer data for this variable - - integer(IK) :: var_type = json_unknown !! variable type - - integer(IK),private :: n_children = 0 !! number of children + class(json_data),allocatable :: data !! the JSON data. + !! when not allocated, + !! it is `json_unknown` end type json_value !********************************************************* @@ -874,6 +905,7 @@ module json_value_module !! children for duplicate keys !other private routines: + procedure,nopass :: destroy_json_data procedure :: name_equal procedure :: name_strings_equal procedure :: json_value_print @@ -1341,15 +1373,8 @@ recursive subroutine json_value_clone_func(from,to,parent,previous,next,children allocate(to) !copy over the data variables: - ! [note: the allocate() statements don't work here for the - ! deferred-length characters in gfortran-4.9] - if (allocated(from%name)) to%name = from%name - if (allocated(from%dbl_value)) allocate(to%dbl_value,source=from%dbl_value) - if (allocated(from%log_value)) allocate(to%log_value,source=from%log_value) - if (allocated(from%str_value)) to%str_value = from%str_value - if (allocated(from%int_value)) allocate(to%int_value,source=from%int_value) - to%var_type = from%var_type - to%n_children = from%n_children + if (allocated(from%name)) to%name = from%name + allocate(to%data, source=from%data) !allocate and associate the pointers as necessary: @@ -1390,18 +1415,13 @@ end subroutine json_value_clone_func ! ! Destroy the data within a [[json_value]], and reset type to `json_unknown`. - pure subroutine destroy_json_data(d) + pure subroutine destroy_json_data(p) implicit none - type(json_value),intent(inout) :: d + type(json_value),intent(inout) :: p - d%var_type = json_unknown - - if (allocated(d%log_value)) deallocate(d%log_value) - if (allocated(d%int_value)) deallocate(d%int_value) - if (allocated(d%dbl_value)) deallocate(d%dbl_value) - if (allocated(d%str_value)) deallocate(d%str_value) + if (allocated(p%data)) deallocate(p%data) end subroutine destroy_json_data !***************************************************************************************** @@ -1424,7 +1444,25 @@ subroutine json_info(json,p,var_type,n_children,name) if (.not. json%exception_thrown .and. associated(p)) then - if (present(var_type)) var_type = p%var_type + if (present(var_type)) then + if (allocated(p%data)) then + associate (data => p%data) + select type (data) + class is (json_string_type); var_type = json_string + class is (json_real_type); var_type = json_real + class is (json_integer_type); var_type = json_integer + class is (json_logical_type); var_type = json_logical + class is (json_array_type); var_type = json_array + class is (json_object_type); var_type = json_object + class is (json_null_type); var_type = json_null + class default; var_type = json_unknown + end select + end associate + else + var_type = json_unknown + end if + end if + if (present(n_children)) n_children = json%count(p) if (present(name)) then if (allocated(p%name)) then @@ -1481,7 +1519,6 @@ subroutine json_string_info(json,p,ilen,max_str_len,found) logical(LK) :: initialized !! if the output array has been sized logical(LK) :: get_max_len !! if we are returning the `max_str_len` logical(LK) :: get_ilen !! if we are returning the `ilen` array - integer(IK) :: var_type !! variable type get_max_len = present(max_str_len) get_ilen = present(ilen) @@ -1493,41 +1530,49 @@ subroutine json_string_info(json,p,ilen,max_str_len,found) if (get_max_len) max_str_len = 0 - select case (p%var_type) + if (allocated(p%data)) then + associate (data => p%data) - case (json_array) ! it's an array + select type (data) - ! call routine for each element - call json%get(p, array_callback=get_string_lengths) + class is (json_array_type) ! it's an array - case default ! not an array + ! call routine for each element + call json%get(p, array_callback=get_string_lengths) - if (json%strict_type_checking) then - ! only allowing strings to be returned - ! as strings, so we can check size directly - call json%info(p,var_type=var_type) - if (var_type==json_string) then - if (allocated(p%str_value) .and. get_max_len) & - max_str_len = len(p%str_value) - else - ! it isn't a string, so there is no length - call json%throw_exception('Error in json_string_info: '//& - 'When strict_type_checking is true '//& - 'the variable must be a character string.',& - found) - end if - else - ! in this case, we have to get the value - ! as a string to know what size it is. - call json%get(p, value=cval) - if (.not. json%exception_thrown) then - if (allocated(cval) .and. get_max_len) & - max_str_len = len(cval) - end if - end if + class default ! not an array - end select + if (json%strict_type_checking) then + ! only allowing strings to be returned + ! as strings, so we can check size directly + select type (data) + class is (json_string_type) + if (allocated(data%value) .and. get_max_len) & + max_str_len = len(data%value) + class default + ! it isn't a string, so there is no length + call json%throw_exception('Error in json_string_info: '//& + 'When strict_type_checking is true '//& + 'the variable must be a character string.',& + found) + end select + else + ! in this case, we have to get the value + ! as a string to know what size it is. + call json%get(p, value=cval) + if (.not. json%exception_thrown) then + if (allocated(cval) .and. get_max_len) & + max_str_len = len(cval) + end if + end if + end select + + end associate + else + call json%throw_exception('Error in json_string_info: '//& + 'JSON data not allocated.') + end if end if if (json%exception_thrown) then @@ -1550,12 +1595,11 @@ subroutine get_string_lengths(json, element, i, count) implicit none class(json_core),intent(inout) :: json - type(json_value),pointer,intent(in) :: element + type(json_value),pointer,intent(in) :: element !! array element integer(IK),intent(in) :: i !! index integer(IK),intent(in) :: count !! size of array character(kind=CK,len=:),allocatable :: cval - integer(IK) :: var_type if (json%exception_thrown) return @@ -1567,23 +1611,30 @@ subroutine get_string_lengths(json, element, i, count) if (json%strict_type_checking) then ! only allowing strings to be returned ! as strings, so we can check size directly - call json%info(element,var_type=var_type) - if (var_type==json_string) then - if (allocated(element%str_value)) then - if (get_max_len) then - if (len(element%str_value)>max_str_len) & - max_str_len = len(element%str_value) - end if - if (get_ilen) ilen(i) = len(element%str_value) - else - if (get_ilen) ilen(i) = 0 - end if + if (allocated(element%data)) then + associate (data => element%data) + select type (data) + class is (json_string_type) + if (allocated(data%value)) then + if (get_max_len) then + if (len(data%value)>max_str_len) & + max_str_len = len(data%value) + end if + if (get_ilen) ilen(i) = len(data%value) + else + if (get_ilen) ilen(i) = 0 + end if + class default + ! it isn't a string, so there is no length + call json%throw_exception('Error in json_string_info: '//& + 'When strict_type_checking is true '//& + 'the array must contain only '//& + 'character strings.',found) + end select + end associate else - ! it isn't a string, so there is no length call json%throw_exception('Error in json_string_info: '//& - 'When strict_type_checking is true '//& - 'the array must contain only '//& - 'character strings.',found) + 'JSON data not allocated.',found) end if else ! in this case, we have to get the value @@ -2264,41 +2315,47 @@ pure recursive subroutine json_value_destroy(json,p,destroy_next) if (allocated(p%name)) deallocate(p%name) - call destroy_json_data(p) - if (associated(p%next)) then ! check for circular references: if (associated(p, p%next)) nullify(p%next) end if - if (associated(p%children)) then - do while (p%n_children > 0) - child => p%children - if (associated(child)) then - p%children => p%children%next - p%n_children = p%n_children - 1 - ! check children for circular references: - circular = (associated(p%children) .and. & - associated(p%children,child)) - call json%destroy(child,destroy_next=.false.) - if (circular) exit - else - ! it is a malformed JSON object. But, we will - ! press ahead with the destroy process, since - ! otherwise, there would be no way to destroy it. - exit - end if - end do - nullify(p%children) - nullify(child) + if (allocated(p%data)) then + associate (data => p%data) + select type (data) + class is (json_data_with_children) + if (associated(p%children)) then + do while (data%n_children > 0) + child => p%children + if (associated(child)) then + p%children => p%children%next + data%n_children = data%n_children - 1 + ! check children for circular references: + circular = (associated(p%children) .and. & + associated(p%children,child)) + call json%destroy(child,destroy_next=.false.) + if (circular) exit + else + ! it is a malformed JSON object. But, we will + ! press ahead with the destroy process, since + ! otherwise, there would be no way to destroy it. + exit + end if + end do + nullify(p%children) + nullify(child) + end if + end select + end associate end if - if (associated(p%next) .and. des_next) call json%destroy(p%next) nullify(p%previous) nullify(p%parent) nullify(p%tail) + call json%destroy_json_data(p) + if (associated(p)) deallocate(p) nullify(p) @@ -2403,7 +2460,12 @@ subroutine json_value_remove(json,p,destroy) end if - parent%n_children = parent%n_children - 1 + associate (parent_data => parent%data) + select type (parent_data) + class is (json_data_with_children) + parent_data%n_children = parent_data%n_children - 1 + end select + end associate end if @@ -2801,65 +2863,11 @@ recursive subroutine check_if_valid(p,require_parent) if (is_valid .and. associated(p)) then - ! data type: - select case (p%var_type) - case(json_null,json_object,json_array) - if (allocated(p%log_value) .or. allocated(p%int_value) .or. & - allocated(p%dbl_value) .or. allocated(p%str_value)) then - error_msg = 'incorrect data allocated for '//& - 'json_null, json_object, or json_array variable type' - is_valid = .false. - return - end if - case(json_logical) - if (.not. allocated(p%log_value)) then - error_msg = 'log_value should be allocated for json_logical variable type' - is_valid = .false. - return - else if (allocated(p%int_value) .or. & - allocated(p%dbl_value) .or. allocated(p%str_value)) then - error_msg = 'incorrect data allocated for json_logical variable type' - is_valid = .false. - return - end if - case(json_integer) - if (.not. allocated(p%int_value)) then - error_msg = 'int_value should be allocated for json_integer variable type' - is_valid = .false. - return - else if (allocated(p%log_value) .or. & - allocated(p%dbl_value) .or. allocated(p%str_value)) then - error_msg = 'incorrect data allocated for json_integer variable type' - is_valid = .false. - return - end if - case(json_real) - if (.not. allocated(p%dbl_value)) then - error_msg = 'dbl_value should be allocated for json_real variable type' - is_valid = .false. - return - else if (allocated(p%log_value) .or. allocated(p%int_value) .or. & - allocated(p%str_value)) then - error_msg = 'incorrect data allocated for json_real variable type' - is_valid = .false. - return - end if - case(json_string) - if (.not. allocated(p%str_value)) then - error_msg = 'str_value should be allocated for json_string variable type' - is_valid = .false. - return - else if (allocated(p%log_value) .or. allocated(p%int_value) .or. & - allocated(p%dbl_value)) then - error_msg = 'incorrect data allocated for json_string variable type' - is_valid = .false. - return - end if - case default + if (.not. allocated(p%data)) then error_msg = 'invalid JSON variable type' is_valid = .false. return - end select + end if if (require_parent .and. .not. associated(p%parent)) then error_msg = 'parent pointer is not associated' @@ -2869,12 +2877,17 @@ recursive subroutine check_if_valid(p,require_parent) if (.not. allocated(p%name)) then if (associated(p%parent)) then - if (p%parent%var_type/=json_array) then - error_msg = 'JSON variable must have a name if not an '//& - 'array element or the root' - is_valid = .false. - return - end if + associate (data => p%parent%data) + select type (data) + class is (json_array_type) + ! array: OK not to have a name + class default + error_msg = 'JSON variable must have a name if not an '//& + 'array element or the root' + is_valid = .false. + return + end select + end associate end if end if @@ -2899,60 +2912,73 @@ recursive subroutine check_if_valid(p,require_parent) if (associated(p%children)) then - if (p%var_type/=json_array .and. p%var_type/=json_object) then - error_msg = 'only arrays and objects can have children' - is_valid = .false. - return - end if + if (allocated(p%data)) then + associate (data => p%data) - ! first validate children pointers: + select type (data) + class is (json_data_with_children) + ! first validate children pointers: - previous => null() - element => p%children - do i = 1_IK, p%n_children - if (.not. associated(element%parent,p)) then - error_msg = 'child''s parent pointer not properly associated' - is_valid = .false. - return - end if - if (i==1 .and. associated(element%previous)) then - error_msg = 'first child shouldn''t have a previous' - is_valid = .false. - return - end if - if (i
1) then
- if (.not. associated(previous,element%previous)) then
- error_msg = 'previous pointer not properly associated'
+ previous => null()
+ element => p%children
+ do i = 1_IK, data%n_children
+ if (.not. associated(element%parent,p)) then
+ error_msg = 'child''s parent pointer not properly associated'
+ is_valid = .false.
+ return
+ end if
+ if (i==1 .and. associated(element%previous)) then
+ error_msg = 'first child shouldn''t have a previous'
+ is_valid = .false.
+ return
+ end if
+ if (i1) then
+ if (.not. associated(previous,element%previous)) then
+ error_msg = 'previous pointer not properly associated'
+ is_valid = .false.
+ return
+ end if
+ end if
+ if (i==data%n_children .and. &
+ .not. associated(element%parent%tail,element)) then
+ error_msg = 'parent''s tail pointer not properly associated'
+ is_valid = .false.
+ return
+ end if
+ if (i element
+ element => element%next
+ end if
+ end do
+
+ !now check all the children:
+ call check_if_valid(p%children,require_parent=.true.)
+
+ class default
+
+ error_msg = 'only arrays and objects can have children'
is_valid = .false.
return
- end if
- end if
- if (i==p%n_children .and. &
- .not. associated(element%parent%tail,element)) then
- error_msg = 'parent''s tail pointer not properly associated'
- is_valid = .false.
- return
- end if
- if (i element
- element => element%next
- end if
- end do
- !now check all the children:
- call check_if_valid(p%children,require_parent=.true.)
+ end select
+ end associate
+ else
+ error_msg = 'JSON data not allocated'
+ is_valid = .false.
+ return
+ end if
end if
end if
@@ -3403,38 +3429,42 @@ subroutine json_value_add_member(json,p,member)
type(json_value),pointer :: member !! the child member
!! to add to `p`
- integer(IK) :: var_type !! variable type of `p`
-
if (.not. json%exception_thrown) then
if (associated(p)) then
- call json%info(p,var_type=var_type)
+ if (allocated(p%data)) then
+ associate (data => p%data)
- select case (var_type)
- case(json_object, json_array)
+ select type (data)
+ class is (json_data_with_children)
- ! associate the parent
- member%parent => p
+ ! associate the parent
+ member%parent => p
- ! add to linked list
- if (associated(p%children)) then
- p%tail%next => member
- member%previous => p%tail
- else
- p%children => member
- member%previous => null() !first in the list
- end if
+ ! add to linked list
+ if (associated(p%children)) then
+ p%tail%next => member
+ member%previous => p%tail
+ else
+ p%children => member
+ member%previous => null() !first in the list
+ end if
- ! new member is now the last one in the list
- p%tail => member
- p%n_children = p%n_children + 1
+ ! new member is now the last one in the list
+ p%tail => member
+ data%n_children = data%n_children + 1
- case default
- call json%throw_exception('Error in json_value_add_member: '//&
- 'can only add child to object or array')
- end select
+ class default
+ call json%throw_exception('Error in json_value_add_member: '//&
+ 'can only add child to object or array')
+ end select
+ end associate
+ else
+ call json%throw_exception('Error in json_value_add_member: '//&
+ 'JSON data not allocated')
+ end if
else
call json%throw_exception('Error in json_value_add_member: '//&
'the pointer is not associated')
@@ -3525,8 +3555,13 @@ subroutine json_value_insert_after(json,p,element)
end do
if (associated(parent)) then
- ! update parent's child counter:
- parent%n_children = parent%n_children + n
+ associate (data => parent%data)
+ select type (data)
+ class is (json_data_with_children)
+ ! update parent's child counter:
+ data%n_children = data%n_children + n
+ end select
+ end associate
! if p is last of parents children then
! also have to update parent tail pointer:
if (associated(parent%tail,p)) then
@@ -3538,8 +3573,12 @@ subroutine json_value_insert_after(json,p,element)
! element is apparently part of an existing list,
! so have to update that as well.
if (associated(element%previous%parent)) then
- element%previous%parent%n_children = &
- element%previous%parent%n_children - n
+ associate (data => element%previous%parent%data)
+ select type (data)
+ class is (json_data_with_children)
+ data%n_children = data%n_children - n
+ end select
+ end associate
element%previous%parent%tail => &
element%previous ! now the last one in the list
else
@@ -3734,13 +3773,17 @@ subroutine json_add_integer_by_path(json,me,path,value,found,was_created)
! being changed (for example, if an array
! is being replaced with a scalar).
- if (p%var_type==json_integer) then
- p%int_value = value
- else
- call json%info(p,name=name)
- call json%create_integer(tmp,value,name)
- call json%replace(p,tmp,destroy=.true.)
- end if
+ if (.not. allocated(p%data)) allocate(json_integer_type :: p%data)
+ associate (data => p%data)
+ select type (data)
+ class is (json_integer_type)
+ data%value = value
+ class default
+ call json%info(p,name=name)
+ call json%create_integer(tmp,value,name)
+ call json%replace(p,tmp,destroy=.true.)
+ end select
+ end associate
end if
@@ -3821,13 +3864,17 @@ subroutine json_add_real_by_path(json,me,path,value,found,was_created)
! being changed (for example, if an array
! is being replaced with a scalar).
- if (p%var_type==json_real) then
- p%dbl_value = value
- else
- call json%info(p,name=name)
- call json%create_real(tmp,value,name)
- call json%replace(p,tmp,destroy=.true.)
- end if
+ if (.not. allocated(p%data)) allocate(json_real_type :: p%data)
+ associate (data => p%data)
+ select type (data)
+ class is (json_real_type)
+ data%value = value
+ class default
+ call json%info(p,name=name)
+ call json%create_real(tmp,value,name)
+ call json%replace(p,tmp,destroy=.true.)
+ end select
+ end associate
end if
@@ -3992,13 +4039,17 @@ subroutine json_add_logical_by_path(json,me,path,value,found,was_created)
! being changed (for example, if an array
! is being replaced with a scalar).
- if (p%var_type==json_logical) then
- p%log_value = value
- else
- call json%info(p,name=name)
- call json%create_logical(tmp,value,name)
- call json%replace(p,tmp,destroy=.true.)
- end if
+ if (.not. allocated(p%data)) allocate(json_logical_type :: p%data)
+ associate (data => p%data)
+ select type (data)
+ class is (json_logical_type)
+ data%value = value
+ class default
+ call json%info(p,name=name)
+ call json%create_logical(tmp,value,name)
+ call json%replace(p,tmp,destroy=.true.)
+ end select
+ end associate
end if
@@ -4082,13 +4133,17 @@ subroutine json_add_string_by_path(json,me,path,value,found,&
! being changed (for example, if an array
! is being replaced with a scalar).
- if (p%var_type==json_string) then
- p%str_value = value
- else
- call json%info(p,name=name)
- call json%create_string(tmp,value,name,trim_str,adjustl_str)
- call json%replace(p,tmp,destroy=.true.)
- end if
+ if (.not. allocated(p%data)) allocate(json_string_type :: p%data)
+ associate (data => p%data)
+ select type (data)
+ class is (json_string_type)
+ data%value = value
+ class default
+ call json%info(p,name=name)
+ call json%create_string(tmp,value,name,trim_str,adjustl_str)
+ call json%replace(p,tmp,destroy=.true.)
+ end select
+ end associate
end if
@@ -5320,8 +5375,20 @@ function json_count(json,p) result(count)
integer(IK) :: count !! number of children in `p`.
if (associated(p)) then
- count = p%n_children
+ if (allocated(p%data)) then
+ associate (data => p%data)
+ select type (data)
+ class is (json_data_with_children)
+ count = data%n_children
+ class default
+ count = 0_IK
+ end select
+ end associate
+ else
+ count = 0_IK
+ end if
else
+ count = 0_IK
call json%throw_exception('Error in json_count: '//&
'pointer is not associated.')
end if
@@ -5461,76 +5528,93 @@ subroutine json_value_get_child_by_index(json, p, idx, child, found)
if (.not. json%exception_thrown) then
- if (associated(p%children)) then
+ if (allocated(p%data)) then
+ associate (data => p%data)
- ! If getting first or last child, we can do this quickly.
- ! Otherwise, traverse the list.
- if (idx==1) then
+ select type (data)
+ class is (json_data_with_children)
- child => p%children ! first one
+ if (associated(p%children)) then
- elseif (idx==p%n_children) then
+ ! If getting first or last child, we can do this quickly.
+ ! Otherwise, traverse the list.
+ if (idx==1) then
- if (associated(p%tail)) then
- child => p%tail ! last one
- else
- call json%throw_exception('Error in json_value_get_child_by_index:'//&
- ' child%tail is not associated.',found)
- end if
+ child => p%children ! first one
- elseif (idx<1 .or. idx>p%n_children) then
+ elseif (idx==data%n_children) then
- call json%throw_exception('Error in json_value_get_child_by_index:'//&
- ' idx is out of range.',found)
+ if (associated(p%tail)) then
+ child => p%tail ! last one
+ else
+ call json%throw_exception('Error in json_value_get_child_by_index:'//&
+ ' child%tail is not associated.',found)
+ end if
- else
+ elseif (idx<1 .or. idx>data%n_children) then
- ! if idx is closer to the end, we traverse the list backward from tail,
- ! otherwise we traverse it forward from children:
+ call json%throw_exception('Error in json_value_get_child_by_index:'//&
+ ' idx is out of range.',found)
- if (p%n_children-idx < idx) then ! traverse backward
+ else
- child => p%tail
+ ! if idx is closer to the end, we traverse the list backward from tail,
+ ! otherwise we traverse it forward from children:
- do i = 1, p%n_children - idx
+ if (data%n_children-idx < idx) then ! traverse backward
- if (associated(child%previous)) then
- child => child%previous
- else
- call json%throw_exception('Error in json_value_get_child_by_index:'//&
- ' child%previous is not associated.',found)
- nullify(child)
- exit
- end if
+ child => p%tail
- end do
+ do i = 1, data%n_children - idx
- else ! traverse forward
+ if (associated(child%previous)) then
+ child => child%previous
+ else
+ call json%throw_exception('Error in json_value_get_child_by_index:'//&
+ ' child%previous is not associated.',found)
+ nullify(child)
+ exit
+ end if
- child => p%children
+ end do
- do i = 1, idx - 1
+ else ! traverse forward
+
+ child => p%children
+
+ do i = 1, idx - 1
+
+ if (associated(child%next)) then
+ child => child%next
+ else
+ call json%throw_exception('Error in json_value_get_child_by_index:'//&
+ ' child%next is not associated.',found)
+ nullify(child)
+ exit
+ end if
+
+ end do
+
+ end if
- if (associated(child%next)) then
- child => child%next
- else
- call json%throw_exception('Error in json_value_get_child_by_index:'//&
- ' child%next is not associated.',found)
- nullify(child)
- exit
end if
- end do
+ else
- end if
+ call json%throw_exception('Error in json_value_get_child_by_index:'//&
+ ' p%children is not associated.',found)
- end if
+ end if
- else
+ class default
+ call json%throw_exception('Error in json_value_get_child_by_index:'//&
+ ' only objects and arrays can have children.',found)
+ end select
+ end associate
+ else
call json%throw_exception('Error in json_value_get_child_by_index:'//&
- ' p%children is not associated.',found)
-
+ ' JSON data not allocated.',found)
end if
! found output:
@@ -5598,8 +5682,9 @@ subroutine json_value_get_child_by_name(json, p, name, child, found)
!! found, no exception will be
!! thrown).
- integer(IK) :: i,n_children
- logical :: error
+ integer(IK) :: i !! counter
+ integer(IK) :: n_children !! number of children in `p`
+ logical :: error !! will be false if `name` is found
nullify(child)
@@ -5608,25 +5693,30 @@ subroutine json_value_get_child_by_name(json, p, name, child, found)
if (associated(p)) then
error = .true. ! will be false if it is found
- if (p%var_type==json_object) then
- n_children = json%count(p)
- child => p%children !start with first one
- do i=1, n_children
- if (.not. associated(child)) then
- call json%throw_exception(&
- 'Error in json_value_get_child_by_name: '//&
- 'Malformed JSON linked list',found)
- exit
- end if
- if (allocated(child%name)) then
- !name string matching routine:
- if (json%name_equal(child,name)) then
- error = .false.
- exit
- end if
- end if
- child => child%next
- end do
+ if (allocated(p%data)) then
+ associate (data => p%data)
+ select type (data)
+ class is (json_object_type)
+ n_children = json%count(p)
+ child => p%children !start with first one
+ do i=1, n_children
+ if (.not. associated(child)) then
+ call json%throw_exception(&
+ 'Error in json_value_get_child_by_name: '//&
+ 'Malformed JSON linked list',found)
+ exit
+ end if
+ if (allocated(child%name)) then
+ !name string matching routine:
+ if (json%name_equal(child,name)) then
+ error = .false.
+ exit
+ end if
+ end if
+ child => child%next
+ end do
+ end select
+ end associate
end if
if (error) then
@@ -5695,6 +5785,7 @@ subroutine json_check_children_for_duplicate_keys(json,p,has_duplicate,name,path
type(json_value),pointer :: child !! pointer to a child of `p`
integer(IK) :: n_children !! number of children of `p`
logical(LK) :: found !! flag for `get_child`
+ integer(IK) :: var_type !! var type of `p`
type :: alloc_str
!! so we can have an array of allocatable strings
@@ -5706,79 +5797,72 @@ subroutine json_check_children_for_duplicate_keys(json,p,has_duplicate,name,path
! initialize:
has_duplicate =.false.
- if (.not. json%exception_thrown) then
+ if (json%exception_thrown) return
+ if (.not. associated(p)) return
- if (associated(p)) then
+ call json%info(p,var_type=var_type,n_children=n_children)
- if (p%var_type==json_object) then
+ if (var_type/=json_object) return
- ! number of items to check:
- n_children = json%count(p)
- allocate(names(n_children))
+ allocate(names(n_children))
- ! first get a list of all the name keys:
- do i=1, n_children
- call json%get_child(p,i,child,found) ! get by index
- if (.not. found) then
- call json%throw_exception(&
- 'Error in json_check_children_for_duplicate_keys: '//&
- 'Malformed JSON linked list')
- exit
- end if
- if (allocated(child%name)) then
- names(i)%str = child%name
- else
- call json%throw_exception(&
- 'Error in json_check_children_for_duplicate_keys: '//&
- 'Object child name is not allocated')
- exit
- end if
- end do
+ ! first get a list of all the name keys:
+ do i=1, n_children
+ call json%get_child(p,i,child,found) ! get by index
+ if (.not. found) then
+ call json%throw_exception(&
+ 'Error in json_check_children_for_duplicate_keys: '//&
+ 'Malformed JSON linked list')
+ exit
+ end if
+ if (allocated(child%name)) then
+ names(i)%str = child%name
+ else
+ call json%throw_exception(&
+ 'Error in json_check_children_for_duplicate_keys: '//&
+ 'Object child name is not allocated')
+ exit
+ end if
+ end do
- if (.not. json%exception_thrown) then
- ! now check the list for duplicates:
- main: do i=1,n_children
- do j=1,i-1
- if (json%name_strings_equal(names(i)%str,names(j)%str)) then
- has_duplicate = .true.
- if (present(name)) then
- name = names(i)%str
- end if
- if (present(path)) then
- call json%get_child(p,names(i)%str,child,found) ! get by name
- if (found) then
- call json%get_path(child,path,found)
- if (.not. found) then
- ! should never happen since we know it is there
- call json%throw_exception(&
- 'Error in json_check_children_for_duplicate_keys: '//&
- 'Could not get path')
- end if
- else
- ! should never happen since we know it is there
- call json%throw_exception(&
- 'Error in json_check_children_for_duplicate_keys: '//&
- 'Could not get child: '//trim(names(i)%str))
- end if
- end if
- exit main
+ if (.not. json%exception_thrown) then
+ ! now check the list for duplicates:
+ main: do i=1,n_children
+ do j=1,i-1
+ if (json%name_strings_equal(names(i)%str,names(j)%str)) then
+ has_duplicate = .true.
+ if (present(name)) then
+ name = names(i)%str
+ end if
+ if (present(path)) then
+ call json%get_child(p,names(i)%str,child,found) ! get by name
+ if (found) then
+ call json%get_path(child,path,found)
+ if (.not. found) then
+ ! should never happen since we know it is there
+ call json%throw_exception(&
+ 'Error in json_check_children_for_duplicate_keys: '//&
+ 'Could not get path')
end if
- end do
- end do main
+ else
+ ! should never happen since we know it is there
+ call json%throw_exception(&
+ 'Error in json_check_children_for_duplicate_keys: '//&
+ 'Could not get child: '//trim(names(i)%str))
+ end if
+ end if
+ exit main
end if
-
- ! cleanup
- do i=1,n_children
- if (allocated(names(i)%str)) deallocate(names(i)%str)
- end do
- if (allocated(names)) deallocate(names)
-
- end if
-
- end if
-
+ end do
+ end do main
end if
+ ! cleanup
+ do i=1,n_children
+ if (allocated(names(i)%str)) deallocate(names(i)%str)
+ end do
+ if (allocated(names)) deallocate(names)
+
end subroutine json_check_children_for_duplicate_keys
!*****************************************************************************************
@@ -5983,7 +6067,7 @@ subroutine json_print_to_filename(json,p,filename)
close(iunit,iostat=istat)
else
call json%throw_exception('Error in json_print_to_filename: could not open file: '//&
- trim(filename))
+ trim(filename))
end if
end subroutine json_print_to_filename
@@ -6096,236 +6180,241 @@ recursive subroutine json_value_print(json,p,iunit,str,indent,&
s_indent = repeat(space, spaces)
end if
- select case (p%var_type)
-
- case (json_object)
+ if (allocated(p%data)) then
+ associate (data => p%data)
- count = json%count(p)
+ select type (data)
- if (count==0) then !special case for empty object
+ class is (json_object_type)
- s = s_indent//start_object//end_object
- call write_it( comma=print_comma )
+ count = json%count(p)
- else
-
- s = s_indent//start_object
- call write_it()
+ if (count==0) then !special case for empty object
- !if an object is in an array, there is an extra tab:
- if (is_array) then
- if ( .not. json%no_whitespace) tab = tab+1
- spaces = tab*json%spaces_per_tab
- end if
+ s = s_indent//start_object//end_object
+ call write_it( comma=print_comma )
- nullify(element)
- element => p%children
- do i = 1, count
+ else
- if (.not. associated(element)) then
- call json%throw_exception('Error in json_value_print: '//&
- 'Malformed JSON linked list')
- return
- end if
+ s = s_indent//start_object
+ call write_it()
- ! print the name
- if (allocated(element%name)) then
- call escape_string(element%name,str_escaped,json%escape_solidus)
- if (json%no_whitespace) then
- !compact printing - no extra space
- s = repeat(space, spaces)//quotation_mark//&
- str_escaped//quotation_mark//colon_char
- call write_it(advance=.false.)
- else
- s = repeat(space, spaces)//quotation_mark//&
- str_escaped//quotation_mark//colon_char//space
- call write_it(advance=.false.)
+ !if an object is in an array, there is an extra tab:
+ if (is_array) then
+ if ( .not. json%no_whitespace) tab = tab+1
+ spaces = tab*json%spaces_per_tab
end if
- else
- call json%throw_exception('Error in json_value_print:'//&
- ' element%name not allocated')
- nullify(element)
- return
- end if
- ! recursive print of the element
- call json%json_value_print(element, iunit=iunit, indent=tab + 1_IK, &
- need_comma=i