@@ -704,6 +704,7 @@ module json_value_module
704
704
procedure :: push_char
705
705
procedure :: get_current_line_from_file_stream
706
706
procedure :: get_current_line_from_file_sequential
707
+ procedure :: convert
707
708
708
709
end type json_core
709
710
! *********************************************************
@@ -5567,8 +5568,8 @@ end subroutine json_get_by_path
5567
5568
! By default, the leaf node and any empty array elements
5568
5569
! are created as `json_null` values.
5569
5570
!
5570
- ! It only works for the default path mode. An error will be
5571
- ! thrown if RFC 6901 mode is enabled .
5571
+ ! It only works for `path_mode=1` or `path_mode=3`.
5572
+ ! An error will be thrown for `path_mode=2` (RFC 6901) .
5572
5573
!
5573
5574
! ### See also
5574
5575
! * [[json_get_by_path]]
@@ -5601,25 +5602,24 @@ subroutine json_create_by_path(json,me,path,p,found,was_created)
5601
5602
create_it= .true. ,&
5602
5603
was_created= was_created)
5603
5604
if (present (p)) p = > tmp
5604
- case (2_IK )
5605
- ! the problem here is there isn't really a way to disambiguate
5606
- ! the array elements, so '/a/0' could be 'a(1)' or 'a.0'.
5607
- call json% throw_exception(' Error in json_create_by_path: ' // &
5608
- ' Create by path not supported in RFC 6901 path mode.' )
5609
- if (present (found)) then
5610
- call json% clear_exceptions()
5611
- found = .false.
5612
- end if
5613
- if (present (was_created)) was_created = .false.
5614
- ! case(3_IK)
5615
- ! call json%json_get_by_path_jsonpath_bracket(me,path,tmp,found,&
5616
- ! create_it=.true.,&
5617
- ! was_created=was_created)
5618
- ! if (present(p)) p => tmp
5605
+ case (3_IK )
5606
+ call json% json_get_by_path_jsonpath_bracket(me,path,tmp,found,&
5607
+ create_it= .true. ,&
5608
+ was_created= was_created)
5609
+ if (present (p)) p = > tmp
5610
+
5619
5611
case default
5620
- call integer_to_string(json% path_mode,int_fmt,path_mode_str)
5621
- call json% throw_exception(' Error in json_create_by_path: Unsupported path_mode: ' // &
5622
- trim (path_mode_str))
5612
+
5613
+ if (json% path_mode== 2_IK ) then
5614
+ ! the problem here is there isn't really a way to disambiguate
5615
+ ! the array elements, so '/a/0' could be 'a(1)' or 'a.0'.
5616
+ call json% throw_exception(' Error in json_create_by_path: ' // &
5617
+ ' Create by path not supported in RFC 6901 path mode.' )
5618
+ else
5619
+ call integer_to_string(json% path_mode,int_fmt,path_mode_str)
5620
+ call json% throw_exception(' Error in json_create_by_path: Unsupported path_mode: ' // &
5621
+ trim (path_mode_str))
5622
+ end if
5623
5623
if (present (found)) then
5624
5624
call json% clear_exceptions()
5625
5625
found = .false.
@@ -5669,6 +5669,7 @@ end subroutine wrap_json_create_by_path
5669
5669
! type(json_value),pointer :: dat,p
5670
5670
! logical :: found
5671
5671
! !...
5672
+ ! call json%initialize(path_mode=1) ! this is the default so not strictly necessary.
5672
5673
! call json%get(dat,'data(2).version',p,found)
5673
5674
! ````
5674
5675
!
@@ -5688,14 +5689,22 @@ end subroutine wrap_json_create_by_path
5688
5689
! Or, the alternate [[json_get_by_path_rfc6901]] could be used.
5689
5690
!
5690
5691
! ### See also
5691
- ! * [[json_get_by_path_rfc6901]] - alternate version with different path convention.
5692
+ ! * [[json_get_by_path_rfc6901]]
5693
+ ! * [[json_get_by_path_jsonpath_bracket]]
5694
+ !
5695
+ ! @note The syntax is inherited from FSON, and is basically a subset
5696
+ ! of JSONPath "dot-notation", with the addition allowance of () for
5697
+ ! array elements.
5692
5698
!
5693
5699
! @note JSON `null` values are used here for unknown variables when `create_it` is True.
5694
5700
! So, it is possible that an existing null variable can be converted to another
5695
5701
! type (object or array) if a child is specified in the path. Doing it this way
5696
5702
! to avoid having to use another type (say `json_unknown`) that would have to be
5697
5703
! converted to null once all the variables have been created (user would have
5698
5704
! had to do this).
5705
+ !
5706
+ ! @warning See (**) in code. I think we need to protect for memory leaks when
5707
+ ! changing the type of a variable that already exists.
5699
5708
5700
5709
subroutine json_get_by_path_default (json ,me ,path ,p ,found ,create_it ,was_created )
5701
5710
@@ -5791,7 +5800,7 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
5791
5800
! What about the case: aaa.bbb(1)(3) ?
5792
5801
! Is that already handled?
5793
5802
5794
- if (p% var_type== json_null) then
5803
+ if (p% var_type== json_null) then ! (**)
5795
5804
! if p was also created, then we need to
5796
5805
! convert it into an object here:
5797
5806
p% var_type = json_object
@@ -5840,7 +5849,7 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
5840
5849
call json% get_child(p, child_i, tmp, child_found)
5841
5850
if (.not. child_found) then
5842
5851
5843
- if (p% var_type== json_null) then
5852
+ if (p% var_type== json_null) then ! (**)
5844
5853
! if p was also created, then we need to
5845
5854
! convert it into an array here:
5846
5855
p% var_type = json_array
@@ -5883,11 +5892,12 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
5883
5892
if (child_i < i) then
5884
5893
nullify(tmp)
5885
5894
if (create) then
5886
- if (p% var_type== json_null) then
5895
+ if (p% var_type== json_null) then ! (**)
5887
5896
! if p was also created, then we need to
5888
5897
! convert it into an object here:
5889
5898
p% var_type = json_object
5890
5899
end if
5900
+
5891
5901
! don't want to throw exceptions in this case
5892
5902
call json% get_child(p, path(child_i:i-1 ), tmp, child_found)
5893
5903
if (.not. child_found) then
@@ -5938,11 +5948,12 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
5938
5948
if (child_i <= length) then
5939
5949
nullify(tmp)
5940
5950
if (create) then
5941
- if (p% var_type== json_null) then
5951
+ if (p% var_type== json_null) then ! (**)
5942
5952
! if p was also created, then we need to
5943
5953
! convert it into an object here:
5944
5954
p% var_type = json_object
5945
5955
end if
5956
+
5946
5957
call json% get_child(p, path(child_i:i-1 ), tmp, child_found)
5947
5958
if (.not. child_found) then
5948
5959
! have to create this child
@@ -6010,14 +6021,17 @@ end subroutine json_get_by_path_default
6010
6021
! ### Example
6011
6022
!
6012
6023
! ````fortran
6024
+ ! type(json_core) :: json
6013
6025
! type(json_value),pointer :: dat,p
6014
6026
! logical :: found
6015
6027
! !...
6028
+ ! call json%initialize(path_mode=2)
6016
6029
! call json%get(dat,'/data/2/version',p,found)
6017
6030
! ````
6018
6031
!
6019
6032
! ### See also
6020
- ! * [[json_get_by_path_default]] - alternate version with different path convention.
6033
+ ! * [[json_get_by_path_default]]
6034
+ ! * [[json_get_by_path_jsonpath_bracket]]
6021
6035
!
6022
6036
! ### Reference
6023
6037
! * [JavaScript Object Notation (JSON) Pointer](https://tools.ietf.org/html/rfc6901)
@@ -6206,6 +6220,12 @@ end subroutine json_get_by_path_rfc6901
6206
6220
! using the "JSON Pointer" path specification defined by the
6207
6221
! JSONPath "bracket-notation".
6208
6222
!
6223
+ ! The first character `$` is optional, and signifies the root
6224
+ ! of the structure. If it is not present, then the first key
6225
+ ! is taken to be in the `me` object.
6226
+ !
6227
+ ! Single or double quotes may be used
6228
+ !
6209
6229
! ### Example
6210
6230
!
6211
6231
! ````fortran
@@ -6214,32 +6234,32 @@ end subroutine json_get_by_path_rfc6901
6214
6234
! logical :: found
6215
6235
! !...
6216
6236
! call json%initialize(path_mode=3)
6217
-
6218
6237
! call json%get(dat,"$['store']['book'][1]['title']",p,found)
6219
6238
! ````
6220
6239
!
6221
- ! The first character `$` is optional, and signifies the root
6222
- ! of the structure. If it is not present, then the first key
6223
- ! is taken to be in the `me` object.
6224
- !
6225
- ! Single or double quotes may be used
6226
- !
6227
6240
! ### See also
6228
- ! * [[json_get_by_path_default]] - subset of JSONPath "dot-notation"
6229
- ! * [[json_get_by_path_rfc6901]] - RFC6901 "JSON pointer"
6241
+ ! * [[json_get_by_path_default]]
6242
+ ! * [[json_get_by_path_rfc6901]]
6230
6243
!
6231
6244
! ### Reference
6232
6245
! * [JSONPath](http://goessner.net/articles/JsonPath/)
6233
6246
!
6234
6247
! @note Uses 1-based array indices (same as [[json_get_by_path_default]],
6235
6248
! but unlike [[json_get_by_path_rfc6901]] which uses 0-based indices).
6236
6249
!
6250
+ ! @note When `create_it=True`, if the variable already exists and is a type
6251
+ ! that is not compatible with the usage in the `path`, then it is
6252
+ ! destroyed and replaced with what is specified in the `path`. Note that
6253
+ ! this applies the all variables in the path as it is created. Currently,
6254
+ ! this behavior is different from [[json_get_by_path_default]].
6255
+ !
6256
+ ! @note JSON `null` values are used here for unknown variables
6257
+ ! when `create_it` is True.
6258
+ !
6237
6259
! @warning Note that if using single quotes, this routine cannot parse
6238
6260
! a key containing `']`. If using double quotes, this routine
6239
6261
! cannot parse a key containing `"]`. If the key contains both
6240
6262
! `']` and `"]`, there is no way to parse it using this routine.
6241
- !
6242
- ! @warning The `create` logic hasn't been added yet !
6243
6263
6244
6264
subroutine json_get_by_path_jsonpath_bracket (json ,me ,path ,p ,found ,create_it ,was_created )
6245
6265
@@ -6278,7 +6298,6 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
6278
6298
integer (IK) :: ilen ! ! length of `path` string
6279
6299
logical (LK) :: double_quotes ! ! if the keys are enclosed in `"`,
6280
6300
! ! rather than `'` tokens.
6281
-
6282
6301
logical (LK) :: create ! ! if the object is to be created
6283
6302
logical (LK) :: created ! ! if `create` is true, then this will be
6284
6303
! ! true if the leaf object had to be created
@@ -6319,6 +6338,7 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
6319
6338
do while (associated (p% parent))
6320
6339
p = > p% parent
6321
6340
end do
6341
+ if (create) created = .false. ! should always exist
6322
6342
end if
6323
6343
6324
6344
! keep trailing space or not:
@@ -6372,9 +6392,9 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
6372
6392
end if
6373
6393
if (iend> istart) then
6374
6394
6375
- ! istart iend
6376
- ! | |
6377
- ! ['abcdefg']
6395
+ ! istart iend
6396
+ ! | |
6397
+ ! ['p'] ['abcdefg']
6378
6398
6379
6399
if (iend> istart+1 ) then
6380
6400
token = path(istart+1 :iend-1 )
@@ -6385,8 +6405,35 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
6385
6405
! the token here if necessary:
6386
6406
if (.not. json% trailing_spaces_significant) &
6387
6407
token = trim (token)
6388
- ! have a token, see if it is valid:
6389
- call json% get_child(p,token,tmp,status_ok)
6408
+
6409
+ if (create) then
6410
+ ! have a token, create it if necessary
6411
+
6412
+ ! we need to convert it into an object here
6413
+ ! (e.g., if p was also just created)
6414
+ ! and destroy its data to prevent a memory leak
6415
+ call json% convert(p,json_object)
6416
+
6417
+ ! don't want to throw exceptions in this case
6418
+ call json% get_child(p,token,tmp,status_ok)
6419
+ if (.not. status_ok) then
6420
+ ! have to create this child
6421
+ ! [make it a null since we don't
6422
+ ! know what it is yet]
6423
+ call json_value_create(tmp)
6424
+ call to_null(tmp,token)
6425
+ call json% add(p,tmp)
6426
+ status_ok = .true.
6427
+ created = .true.
6428
+ else
6429
+ ! it was already there.
6430
+ created = .false.
6431
+ end if
6432
+ else
6433
+ ! have a token, see if it is valid:
6434
+ call json% get_child(p,token,tmp,status_ok)
6435
+ end if
6436
+
6390
6437
if (status_ok) then
6391
6438
! it was found
6392
6439
p = > tmp
@@ -6429,10 +6476,45 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
6429
6476
call string_to_integer(token,ival,status_ok)
6430
6477
if (status_ok) status_ok = ival> 0 ! assuming 1-based array indices
6431
6478
end if
6479
+
6432
6480
if (status_ok) then
6433
- ! have a valid integer to use as an index, so
6481
+
6482
+ ! have a valid integer to use as an index
6434
6483
! see if this element is really there:
6435
6484
call json% get_child(p,ival,tmp,status_ok)
6485
+
6486
+ if (create .and. .not. status_ok) then
6487
+
6488
+ ! have to create it:
6489
+
6490
+ if (.not. (p% var_type== json_object .or. p% var_type== json_array)) then
6491
+ ! we need to convert it into an array here
6492
+ ! (e.g., if p was also just created)
6493
+ ! and destroy its data to prevent a memory leak
6494
+ call json% convert(p,json_array)
6495
+ end if
6496
+
6497
+ ! have to create this element
6498
+ ! [make it a null]
6499
+ ! (and any missing ones before it)
6500
+ do j = 1 , ival
6501
+ nullify(tmp)
6502
+ call json% get_child(p, j, tmp, status_ok)
6503
+ if (.not. status_ok) then
6504
+ call json_value_create(tmp)
6505
+ call to_null(tmp) ! array element doesn't need a name
6506
+ call json% add(p,tmp)
6507
+ if (j== ival) created = .true.
6508
+ else
6509
+ if (j== ival) created = .false.
6510
+ end if
6511
+ end do
6512
+ status_ok = .true.
6513
+
6514
+ else
6515
+ created = .false.
6516
+ end if
6517
+
6436
6518
if (status_ok) then
6437
6519
! found it
6438
6520
p = > tmp
@@ -6494,13 +6576,66 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
6494
6576
if (present (found)) found = .true.
6495
6577
end if
6496
6578
6579
+ ! if it had to be created:
6580
+ if (present (was_created)) was_created = created
6581
+
6497
6582
else
6498
6583
if (present (found)) found = .false.
6584
+ if (present (was_created)) was_created = .false.
6499
6585
end if
6500
6586
6501
6587
end subroutine json_get_by_path_jsonpath_bracket
6502
6588
! *****************************************************************************************
6503
6589
6590
+ ! *****************************************************************************************
6591
+ ! >
6592
+ ! Convert an existing JSON variable `p` to a different variable type.
6593
+ ! The existing variable (and its children) is destroyed. It is replaced
6594
+ ! in the structure by a new variable of type `var_type`
6595
+ ! (which can be a `json_null`, `json_object` or `json_array`).
6596
+ !
6597
+ ! @note This is an internal routine used when creating variables by path.
6598
+
6599
+ subroutine convert (json ,p ,var_type )
6600
+
6601
+ implicit none
6602
+
6603
+ class(json_core),intent (inout ) :: json
6604
+ type (json_value),pointer :: p ! ! the variable to convert
6605
+ integer (IK),intent (in ) :: var_type ! ! the variable type to convert `p` to
6606
+
6607
+ type (json_value),pointer :: tmp ! ! temporary variable
6608
+ character (kind= CK,len= :),allocatable :: name ! ! the name of a JSON variable
6609
+
6610
+ logical :: convert_it ! ! if `p` needs to be converted
6611
+
6612
+ convert_it = p% var_type /= var_type
6613
+
6614
+ if (convert_it) then
6615
+
6616
+ call json% info(p,name= name) ! get existing name
6617
+
6618
+ select case (var_type)
6619
+ case (json_object)
6620
+ call json% create_object(tmp,name)
6621
+ case (json_array)
6622
+ call json% create_array(tmp,name)
6623
+ case (json_null)
6624
+ call json% create_null(tmp,name)
6625
+ case default
6626
+ call json% throw_exception(' Error in convert: invalid var_type value.' )
6627
+ return
6628
+ end select
6629
+
6630
+ call json% replace (p,tmp,destroy= .true. )
6631
+ p = > tmp
6632
+ nullify(tmp)
6633
+
6634
+ end if
6635
+
6636
+ end subroutine convert
6637
+ ! *****************************************************************************************
6638
+
6504
6639
! *****************************************************************************************
6505
6640
! >
6506
6641
! Alternate version of [[json_get_by_path]] where "path" is kind=CDK.
0 commit comments