@@ -704,6 +704,7 @@ module json_value_module
704704 procedure :: push_char
705705 procedure :: get_current_line_from_file_stream
706706 procedure :: get_current_line_from_file_sequential
707+ procedure :: convert
707708
708709 end type json_core
709710 ! *********************************************************
@@ -5567,8 +5568,8 @@ end subroutine json_get_by_path
55675568! By default, the leaf node and any empty array elements
55685569! are created as `json_null` values.
55695570!
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) .
55725573!
55735574! ### See also
55745575! * [[json_get_by_path]]
@@ -5601,25 +5602,24 @@ subroutine json_create_by_path(json,me,path,p,found,was_created)
56015602 create_it= .true. ,&
56025603 was_created= was_created)
56035604 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+
56195611 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
56235623 if (present (found)) then
56245624 call json% clear_exceptions()
56255625 found = .false.
@@ -5669,6 +5669,7 @@ end subroutine wrap_json_create_by_path
56695669! type(json_value),pointer :: dat,p
56705670! logical :: found
56715671! !...
5672+ ! call json%initialize(path_mode=1) ! this is the default so not strictly necessary.
56725673! call json%get(dat,'data(2).version',p,found)
56735674! ````
56745675!
@@ -5688,14 +5689,22 @@ end subroutine wrap_json_create_by_path
56885689! Or, the alternate [[json_get_by_path_rfc6901]] could be used.
56895690!
56905691! ### 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.
56925698!
56935699! @note JSON `null` values are used here for unknown variables when `create_it` is True.
56945700! So, it is possible that an existing null variable can be converted to another
56955701! type (object or array) if a child is specified in the path. Doing it this way
56965702! to avoid having to use another type (say `json_unknown`) that would have to be
56975703! converted to null once all the variables have been created (user would have
56985704! 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.
56995708
57005709 subroutine json_get_by_path_default (json ,me ,path ,p ,found ,create_it ,was_created )
57015710
@@ -5791,7 +5800,7 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
57915800 ! What about the case: aaa.bbb(1)(3) ?
57925801 ! Is that already handled?
57935802
5794- if (p% var_type== json_null) then
5803+ if (p% var_type== json_null) then ! (**)
57955804 ! if p was also created, then we need to
57965805 ! convert it into an object here:
57975806 p% var_type = json_object
@@ -5840,7 +5849,7 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
58405849 call json% get_child(p, child_i, tmp, child_found)
58415850 if (.not. child_found) then
58425851
5843- if (p% var_type== json_null) then
5852+ if (p% var_type== json_null) then ! (**)
58445853 ! if p was also created, then we need to
58455854 ! convert it into an array here:
58465855 p% var_type = json_array
@@ -5883,11 +5892,12 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
58835892 if (child_i < i) then
58845893 nullify(tmp)
58855894 if (create) then
5886- if (p% var_type== json_null) then
5895+ if (p% var_type== json_null) then ! (**)
58875896 ! if p was also created, then we need to
58885897 ! convert it into an object here:
58895898 p% var_type = json_object
58905899 end if
5900+
58915901 ! don't want to throw exceptions in this case
58925902 call json% get_child(p, path(child_i:i-1 ), tmp, child_found)
58935903 if (.not. child_found) then
@@ -5938,11 +5948,12 @@ subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created)
59385948 if (child_i <= length) then
59395949 nullify(tmp)
59405950 if (create) then
5941- if (p% var_type== json_null) then
5951+ if (p% var_type== json_null) then ! (**)
59425952 ! if p was also created, then we need to
59435953 ! convert it into an object here:
59445954 p% var_type = json_object
59455955 end if
5956+
59465957 call json% get_child(p, path(child_i:i-1 ), tmp, child_found)
59475958 if (.not. child_found) then
59485959 ! have to create this child
@@ -6010,14 +6021,17 @@ end subroutine json_get_by_path_default
60106021! ### Example
60116022!
60126023! ````fortran
6024+ ! type(json_core) :: json
60136025! type(json_value),pointer :: dat,p
60146026! logical :: found
60156027! !...
6028+ ! call json%initialize(path_mode=2)
60166029! call json%get(dat,'/data/2/version',p,found)
60176030! ````
60186031!
60196032! ### 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]]
60216035!
60226036! ### Reference
60236037! * [JavaScript Object Notation (JSON) Pointer](https://tools.ietf.org/html/rfc6901)
@@ -6206,6 +6220,12 @@ end subroutine json_get_by_path_rfc6901
62066220! using the "JSON Pointer" path specification defined by the
62076221! JSONPath "bracket-notation".
62086222!
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+ !
62096229! ### Example
62106230!
62116231! ````fortran
@@ -6214,32 +6234,32 @@ end subroutine json_get_by_path_rfc6901
62146234! logical :: found
62156235! !...
62166236! call json%initialize(path_mode=3)
6217-
62186237! call json%get(dat,"$['store']['book'][1]['title']",p,found)
62196238! ````
62206239!
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- !
62276240! ### 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]]
62306243!
62316244! ### Reference
62326245! * [JSONPath](http://goessner.net/articles/JsonPath/)
62336246!
62346247! @note Uses 1-based array indices (same as [[json_get_by_path_default]],
62356248! but unlike [[json_get_by_path_rfc6901]] which uses 0-based indices).
62366249!
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+ !
62376259! @warning Note that if using single quotes, this routine cannot parse
62386260! a key containing `']`. If using double quotes, this routine
62396261! cannot parse a key containing `"]`. If the key contains both
62406262! `']` and `"]`, there is no way to parse it using this routine.
6241- !
6242- ! @warning The `create` logic hasn't been added yet !
62436263
62446264 subroutine json_get_by_path_jsonpath_bracket (json ,me ,path ,p ,found ,create_it ,was_created )
62456265
@@ -6278,7 +6298,6 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
62786298 integer (IK) :: ilen ! ! length of `path` string
62796299 logical (LK) :: double_quotes ! ! if the keys are enclosed in `"`,
62806300 ! ! rather than `'` tokens.
6281-
62826301 logical (LK) :: create ! ! if the object is to be created
62836302 logical (LK) :: created ! ! if `create` is true, then this will be
62846303 ! ! 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_
63196338 do while (associated (p% parent))
63206339 p = > p% parent
63216340 end do
6341+ if (create) created = .false. ! should always exist
63226342 end if
63236343
63246344 ! keep trailing space or not:
@@ -6372,9 +6392,9 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
63726392 end if
63736393 if (iend> istart) then
63746394
6375- ! istart iend
6376- ! | |
6377- ! ['abcdefg']
6395+ ! istart iend
6396+ ! | |
6397+ ! ['p'] ['abcdefg']
63786398
63796399 if (iend> istart+1 ) then
63806400 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_
63856405 ! the token here if necessary:
63866406 if (.not. json% trailing_spaces_significant) &
63876407 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+
63906437 if (status_ok) then
63916438 ! it was found
63926439 p = > tmp
@@ -6429,10 +6476,45 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
64296476 call string_to_integer(token,ival,status_ok)
64306477 if (status_ok) status_ok = ival> 0 ! assuming 1-based array indices
64316478 end if
6479+
64326480 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
64346483 ! see if this element is really there:
64356484 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+
64366518 if (status_ok) then
64376519 ! found it
64386520 p = > tmp
@@ -6494,13 +6576,66 @@ subroutine json_get_by_path_jsonpath_bracket(json,me,path,p,found,create_it,was_
64946576 if (present (found)) found = .true.
64956577 end if
64966578
6579+ ! if it had to be created:
6580+ if (present (was_created)) was_created = created
6581+
64976582 else
64986583 if (present (found)) found = .false.
6584+ if (present (was_created)) was_created = .false.
64996585 end if
65006586
65016587 end subroutine json_get_by_path_jsonpath_bracket
65026588! *****************************************************************************************
65036589
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+
65046639! *****************************************************************************************
65056640! >
65066641! Alternate version of [[json_get_by_path]] where "path" is kind=CDK.
0 commit comments