Skip to content

Commit 331bde2

Browse files
committed
Merge pull request #144 from jacobwilliams/traverse
New routine to traverse all nodes of a JSON structure. Fixes #140
2 parents 328d6b9 + 8200541 commit 331bde2

File tree

2 files changed

+177
-3
lines changed

2 files changed

+177
-3
lines changed

src/json_module.F90

+56-3
Original file line numberDiff line numberDiff line change
@@ -456,17 +456,25 @@ module json_module
456456
!*************************************************************************************
457457

458458
!*************************************************************************************
459-
!>
460-
! Array element callback function. Used by [[json_get_array]].
461-
462459
abstract interface
460+
463461
subroutine array_callback_func(element, i, count)
462+
!! Array element callback function. Used by [[json_get_array]]
464463
import :: json_value,IK
465464
implicit none
466465
type(json_value), pointer,intent(in) :: element
467466
integer(IK),intent(in) :: i !index
468467
integer(IK),intent(in) :: count !size of array
469468
end subroutine array_callback_func
469+
470+
subroutine traverse_callback_func(p,finished)
471+
!! Callback function used by [[json_traverse]]
472+
import :: json_value,LK
473+
implicit none
474+
type(json_value),pointer,intent(in) :: p
475+
logical(LK),intent(out) :: finished
476+
end subroutine traverse_callback_func
477+
470478
end interface
471479
!*************************************************************************************
472480

@@ -827,6 +835,7 @@ end subroutine array_callback_func
827835
public :: json_remove ! remove from a JSON structure
828836
public :: json_remove_if_present ! remove from a JSON structure (if it is present)
829837
public :: json_update ! update a value in a JSON structure
838+
public :: json_traverse ! to traverse all elements of a JSON structure
830839
public :: json_print_error_message !
831840
public :: to_unicode ! Function to convert from 'DEFAULT' to 'ISO_10646' strings
832841

@@ -4959,6 +4968,50 @@ subroutine json_get_array(me, array_callback)
49594968
end subroutine json_get_array
49604969
!*****************************************************************************************
49614970

4971+
!*****************************************************************************************
4972+
!> author: Jacob Williams
4973+
! date: 09/02/2015
4974+
!
4975+
! Traverse a JSON structure.
4976+
! This routine calls the user-specified [[traverse_callback_func]]
4977+
! for each element of the structure.
4978+
!
4979+
recursive subroutine json_traverse(me,traverse_callback)
4980+
4981+
implicit none
4982+
4983+
type(json_value),pointer,intent(in) :: me
4984+
procedure(traverse_callback_func) :: traverse_callback
4985+
4986+
type(json_value),pointer :: element !! a child element
4987+
integer(IK) :: i !! counter
4988+
integer(IK) :: icount !! number of children
4989+
logical(LK) :: finished !! can be used to stop the process
4990+
4991+
if (exception_thrown) return
4992+
4993+
call traverse_callback(me,finished) ! first call for this object
4994+
if (finished) return
4995+
4996+
!for arrays and objects, have to also call for all children:
4997+
if (me%var_type==json_array .or. me%var_type==json_object) then
4998+
4999+
icount = json_count(me) ! number of children
5000+
if (icount>0) then
5001+
element => me%children ! first one
5002+
do i = 1, icount ! call for each child
5003+
call json_traverse(element,traverse_callback)
5004+
if (finished) exit
5005+
element => element%next
5006+
end do
5007+
end if
5008+
nullify(element)
5009+
5010+
end if
5011+
5012+
end subroutine json_traverse
5013+
!*****************************************************************************************
5014+
49625015
!*****************************************************************************************
49635016
!>
49645017
! This routine calls the user-supplied array_callback subroutine

src/tests/jf_test_14.f90

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
!*****************************************************************************************
2+
!> author: Jacob Williams
3+
! date: 09/02/2015
4+
!
5+
! Module for the 14th unit test.
6+
7+
module jf_test_14_mod
8+
9+
use json_module
10+
use, intrinsic :: iso_fortran_env , only: error_unit,output_unit
11+
12+
implicit none
13+
14+
character(len=*),parameter :: dir = '../files/inputs/' !! working directory
15+
character(len=*),parameter :: filename1 = 'test1.json' !! the file to read
16+
integer :: icount = 0 !! a count of the number of "name" variables found
17+
18+
contains
19+
20+
subroutine test_14(error_cnt)
21+
22+
!! Tests the traversal of a JSON structure
23+
!!
24+
!! It traverses the structure, looks for all "name" variables, and changes the name.
25+
26+
implicit none
27+
28+
integer,intent(out) :: error_cnt !! report number of errors to caller
29+
30+
type(json_value),pointer :: json
31+
32+
write(error_unit,'(A)') ''
33+
write(error_unit,'(A)') '================================='
34+
write(error_unit,'(A)') ' TEST 14'
35+
write(error_unit,'(A)') '================================='
36+
write(error_unit,'(A)') ''
37+
38+
error_cnt = 0
39+
icount = 0 !number of name changes (should be 2)
40+
41+
call json_initialize() !initialize the module
42+
43+
call json_parse(dir//filename1,json) !read the file
44+
if (json_failed()) then
45+
call json_print_error_message(error_unit)
46+
error_cnt = error_cnt + 1
47+
end if
48+
49+
call json_traverse(json,rename) !traverse all nodes in the structure
50+
if (json_failed()) then
51+
call json_print_error_message(error_unit)
52+
error_cnt = error_cnt + 1
53+
end if
54+
55+
if (icount/=2) then
56+
write(error_unit,'(A)') 'Error: should be 2 "name" variables in this file: '//filename1
57+
error_cnt = error_cnt + 1
58+
end if
59+
60+
if (error_cnt==0) then
61+
write(error_unit,'(A)') ''
62+
write(error_unit,'(A)') ' All names changed to Fred:'
63+
write(error_unit,'(A)') ''
64+
call json_print(json,output_unit)
65+
write(error_unit,'(A)') ''
66+
end if
67+
68+
call json_destroy(json) !clean up
69+
if (json_failed()) then
70+
call json_print_error_message(error_unit)
71+
error_cnt = error_cnt + 1
72+
end if
73+
74+
end subroutine test_14
75+
76+
subroutine rename(p,finished) !! change all "name" variable values to "Fred"
77+
78+
implicit none
79+
80+
type(json_value),pointer,intent(in) :: p
81+
logical,intent(out) :: finished
82+
83+
integer :: var_type
84+
character(kind=CK,len=:),allocatable :: str
85+
logical :: found
86+
87+
!get info about this variable:
88+
call json_info(p,var_type=var_type,name=str)
89+
90+
!it must be a string named "name":
91+
if (var_type==json_string .and. str=='name') then
92+
call json_get(p,'@',str) ! get original name
93+
call json_update(p,'@','Fred',found) !change it
94+
write(error_unit,'(A)') str//' name changed'
95+
icount = icount + 1
96+
end if
97+
98+
!cleanup:
99+
if (allocated(str)) deallocate(str)
100+
101+
!always false, since we want to traverse all nodes:
102+
finished = .false.
103+
104+
end subroutine rename
105+
106+
end module jf_test_14_mod
107+
!*****************************************************************************************
108+
109+
!*****************************************************************************************
110+
program jf_test_14
111+
112+
!! 14th unit test.
113+
114+
use jf_test_14_mod, only: test_14
115+
implicit none
116+
integer :: n_errors
117+
call test_14(n_errors)
118+
if ( n_errors /= 0) stop 1
119+
120+
end program jf_test_14
121+
!*****************************************************************************************

0 commit comments

Comments
 (0)