Skip to content

Commit d231285

Browse files
Merge pull request #446 from jacobwilliams/444-new-integer-parsing-option
added a new option for integer parsing.
2 parents ca194e9 + 330bf2b commit d231285

File tree

6 files changed

+169
-9
lines changed

6 files changed

+169
-9
lines changed

src/json_initialize_arguments.inc

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,17 @@ integer(IK),intent(in),optional :: non_normal_mode
9696
!! "Infinity", "-Infinity") [default]
9797
!! * 2 : as JSON `null` values
9898
logical(LK),intent(in),optional :: use_quiet_nan
99-
!! if true [default], `null_to_real_mode=2`
100-
!! and [[string_to_real]] will use
101-
!! `ieee_quiet_nan` for NaN values. If false,
102-
!! `ieee_signaling_nan` will be used.
99+
!! * If true [default], `null_to_real_mode=2`
100+
!! and [[string_to_real]] will use
101+
!! `ieee_quiet_nan` for NaN values.
102+
!! * If false,
103+
!! `ieee_signaling_nan` will be used.
104+
logical(LK),intent(in),optional :: strict_integer_type_checking
105+
!! * If false, when parsing JSON, if an integer numeric value
106+
!! cannot be converted to an integer (`integer(IK)`),
107+
!! then an attempt is then make to convert it
108+
!! to a real (`real(RK)`).
109+
!! * If true, an exception will be raised if the integer
110+
!! value cannot be read.
111+
!!
112+
!! (default is true)

src/json_initialize_dummy_arguments.inc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ escape_solidus,&
2121
stop_on_error,&
2222
null_to_real_mode,&
2323
non_normal_mode,&
24-
use_quiet_nan &
24+
use_quiet_nan, &
25+
strict_integer_type_checking &

src/json_value_module.F90

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,14 @@ module json_value_module
278278
!! `ieee_quiet_nan` for NaN values. If false,
279279
!! `ieee_signaling_nan` will be used.
280280

281+
logical(LK) :: strict_integer_type_checking = .true.
282+
!! * If false, when parsing JSON, if an integer numeric value
283+
!! cannot be converted to an integer (`integer(IK)`),
284+
!! then an attempt is then make to convert it
285+
!! to a real (`real(RK)`).
286+
!! * If true [default], an exception will be raised if an integer
287+
!! value cannot be read when parsing JSON.
288+
281289
integer :: ichunk = 0 !! index in `chunk` for [[pop_char]]
282290
!! when `use_unformatted_stream=True`
283291
integer :: filesize = 0 !! the file size when when `use_unformatted_stream=True`
@@ -1125,6 +1133,10 @@ subroutine json_initialize(me,&
11251133
me%use_quiet_nan = use_quiet_nan
11261134
end if
11271135

1136+
if (present(strict_integer_type_checking)) then
1137+
me%strict_integer_type_checking = strict_integer_type_checking
1138+
end if
1139+
11281140
!Set the format for real numbers:
11291141
! [if not changing it, then it remains the same]
11301142

@@ -6772,6 +6784,7 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
67726784
logical(LK) :: created !! if `create` is true, then this will be
67736785
!! true if the leaf object had to be created
67746786
integer(IK) :: j !! counter of children when creating object
6787+
logical(LK) :: status_ok !! integer to string conversion flag
67756788

67766789
nullify(p)
67776790

@@ -6877,7 +6890,13 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
68776890
exit
68786891
end if
68796892
array = .false.
6880-
child_i = json%string_to_int(path(child_i:i-1))
6893+
call string_to_integer(path(child_i:i-1),child_i,status_ok)
6894+
if (.not. status_ok) then
6895+
call json%throw_exception('Error in json_get_by_path_default:'//&
6896+
' Could not convert array index to integer: '//&
6897+
trim(path(child_i:i-1)),found)
6898+
exit
6899+
end if
68816900

68826901
nullify(tmp)
68836902
if (create) then
@@ -7991,8 +8010,8 @@ function string_to_int(json,str) result(ival)
79918010
if (.not. status_ok) then
79928011
ival = 0
79938012
call json%throw_exception('Error in string_to_int: '//&
7994-
'string cannot be converted to an integer: '//&
7995-
trim(str))
8013+
'string cannot be converted to an integer: '//&
8014+
trim(str))
79968015
end if
79978016

79988017
else
@@ -11128,6 +11147,8 @@ subroutine parse_number(json, unit, str, value)
1112811147
type(json_value),pointer :: value
1112911148

1113011149
character(kind=CK,len=:),allocatable :: tmp !! temp string
11150+
character(kind=CK,len=:),allocatable :: saved_err_message !! temp error message for
11151+
!! string to int conversion
1113111152
character(kind=CK,len=1) :: c !! character returned by [[pop_char]]
1113211153
logical(LK) :: eof !! end of file flag
1113311154
real(RK) :: rval !! real value
@@ -11187,9 +11208,31 @@ subroutine parse_number(json, unit, str, value)
1118711208

1118811209
!string to value:
1118911210
if (is_integer) then
11211+
! it is an integer:
1119011212
ival = json%string_to_int(tmp)
11191-
call json%to_integer(value,ival)
11213+
11214+
if (json%exception_thrown .and. .not. json%strict_integer_type_checking) then
11215+
! if it couldn't be converted to an integer,
11216+
! then try to convert it to a real value and see if that works
11217+
11218+
saved_err_message = json%err_message ! keep the original error message
11219+
call json%clear_exceptions() ! clear exceptions
11220+
rval = json%string_to_dble(tmp)
11221+
if (json%exception_thrown) then
11222+
! restore original error message and continue
11223+
json%err_message = saved_err_message
11224+
call json%to_integer(value,ival) ! just so we have something
11225+
else
11226+
! in this case, we return a real
11227+
call json%to_real(value,rval)
11228+
end if
11229+
11230+
else
11231+
call json%to_integer(value,ival)
11232+
end if
11233+
1119211234
else
11235+
! it is a real:
1119311236
rval = json%string_to_dble(tmp)
1119411237
call json%to_real(value,rval)
1119511238
end if

src/tests/jf_test_43.F90

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
!*****************************************************************************************
2+
!>
3+
! Module for the 43nd unit test
4+
5+
module jf_test_43_mod
6+
7+
use json_module, CK => json_CK, IK => json_IK, RK => json_RK, LK => json_LK
8+
use, intrinsic :: iso_fortran_env , only: error_unit, output_unit
9+
10+
implicit none
11+
12+
private
13+
public :: test_43
14+
15+
contains
16+
17+
subroutine test_43(error_cnt)
18+
19+
!! integer too large for default integer kind
20+
21+
implicit none
22+
23+
integer,intent(out) :: error_cnt !! error counter
24+
25+
character(kind=CK,len=*),parameter :: str = CK_'{ "x": 2147483648 }'
26+
27+
type(json_file) :: json
28+
logical :: found
29+
real(RK) :: x
30+
31+
write(error_unit,'(A)') ''
32+
write(error_unit,'(A)') '================================='
33+
write(error_unit,'(A)') ' TEST 43'
34+
write(error_unit,'(A)') '================================='
35+
write(error_unit,'(A)') ''
36+
37+
error_cnt = 0
38+
39+
! enable the feature to try to parse an int
40+
! as a real if it fails to be parsed as an int
41+
call json%initialize(strict_integer_type_checking=.false.)
42+
43+
write(error_unit,'(A)') ''
44+
write(error_unit,'(A)') 'JSON string to parse... '
45+
write(error_unit,'(A)') ''
46+
write(error_unit,'(A)') str
47+
48+
! parse the json string:
49+
write(error_unit,'(A)') ''
50+
write(error_unit,'(A)') 'parsing string... '
51+
call json%deserialize(str)
52+
if (json%failed()) then
53+
call json%print_error_message(error_unit)
54+
error_cnt = error_cnt + 1
55+
end if
56+
write(error_unit,'(A)') ''
57+
write(error_unit,'(A)') 'printing...'
58+
call json%print(int(error_unit,IK))
59+
write(error_unit,'(A)') ''
60+
61+
call json%get('x',x,found)
62+
if (found) then
63+
write(error_unit,'(A,1X,F32.17)') 'x = ', x
64+
else
65+
write(error_unit,'(A)') 'Error: x not found in string'
66+
error_cnt = error_cnt + 1
67+
end if
68+
69+
if (json%failed()) then
70+
call json%print_error_message(error_unit)
71+
error_cnt = error_cnt + 1
72+
end if
73+
74+
if (error_cnt==0) then
75+
write(error_unit,'(A)') 'Success!'
76+
else
77+
write(error_unit,'(A)') 'Failed!'
78+
end if
79+
80+
end subroutine test_43
81+
82+
end module jf_test_43_mod
83+
!*****************************************************************************************
84+
85+
#ifndef INTEGRATED_TESTS
86+
!*****************************************************************************************
87+
program jf_test_43
88+
89+
!! 43nd unit test.
90+
91+
use jf_test_43_mod , only: test_43
92+
implicit none
93+
integer :: n_errors
94+
n_errors = 0
95+
call test_43(n_errors)
96+
if (n_errors /= 0) stop 1
97+
98+
end program jf_test_43
99+
!*****************************************************************************************
100+
#endif
101+
102+
103+
104+

visual_studio/jsonfortrantest/jsonfortrantest.f90

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ program jsonfortrantest
9898
call test_40(n_errors); if (n_errors /= 0) stop 1
9999
call test_41(n_errors); if (n_errors /= 0) stop 1
100100
call test_42(n_errors); if (n_errors /= 0) stop 1
101+
call test_43(n_errors); if (n_errors /= 0) stop 1
101102

102103
end program jsonfortrantest
103104
!*****************************************************************************************

visual_studio/jsonfortrantest/jsonfortrantest.vfproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,6 @@
8888
<File RelativePath="..\..\src\tests\jf_test_40.F90"/>
8989
<File RelativePath="..\..\src\tests\jf_test_41.F90"/>
9090
<File RelativePath="..\..\src\tests\jf_test_42.F90"/>
91+
<File RelativePath="..\..\src\tests\jf_test_43.F90"/>
9192
<File RelativePath=".\jsonfortrantest.f90"/></Filter></Files>
9293
<Globals/></VisualStudioProject>

0 commit comments

Comments
 (0)