Returns the json_value pointer given the path string.
type(json_core) :: json
type(json_value),pointer :: dat,p
logical :: found
!...
call json%initialize(path_mode=1) ! this is the default so not strictly necessary.
call json%get(dat,'data(2).version',p,found)
The syntax used here is a subset of the http://goessner.net/articles/JsonPath/ “dot–notation”. The following special characters are used to denote paths:
$
- root@
- this.
- child object member (note this can be changed using json%path_separator
)[]
or ()
- child array element (note that indices are 1-based)Thus, if any of these characters are present in the name key,
this routine cannot be used to get the value.
In that case, the get_child
methods would need to be used.
Or, the alternate json_get_by_path_rfc6901 could be used.
Note
The syntax is inherited from FSON, and is basically a subset of JSONPath “dot-notation”, with the additional allowance of () for array elements.
Note
JSON null
values are used here for unknown variables when create_it
is True.
So, it is possible that an existing null variable can be converted to another
type (object or array) if a child is specified in the path. Doing it this way
to avoid having to use another type (say json_unknown
) that would have to be
converted to null once all the variables have been created (user would have
had to do this).
Warning
See (**) in code. I think we need to protect for memory leaks when changing the type of a variable that already exists.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(json_core), | intent(inout) | :: | json | |||
type(json_value), | intent(in), | pointer | :: | me |
a JSON linked list |
|
character(kind=CK, len=*), | intent(in) | :: | path |
path to the variable |
||
type(json_value), | intent(out), | pointer | :: | p |
pointer to the variable
specify by |
|
logical(kind=LK), | intent(out), | optional | :: | found |
true if it was found |
|
logical(kind=LK), | intent(in), | optional | :: | create_it |
if a variable is not present
in the path, then it is created.
the leaf node is returned as
a |
|
logical(kind=LK), | intent(out), | optional | :: | was_created |
if |
subroutine json_get_by_path_default(json,me,path,p,found,create_it,was_created) implicit none class(json_core),intent(inout) :: json type(json_value),pointer,intent(in) :: me !! a JSON linked list character(kind=CK,len=*),intent(in) :: path !! path to the variable type(json_value),pointer,intent(out) :: p !! pointer to the variable !! specify by `path` logical(LK),intent(out),optional :: found !! true if it was found logical(LK),intent(in),optional :: create_it !! if a variable is not present !! in the path, then it is created. !! the leaf node is returned as !! a `null` json type and can be !! changed by the caller. logical(LK),intent(out),optional :: was_created !! if `create_it` is true, this !! will be true if the variable !! was actually created. Otherwise !! it will be false. integer(IK) :: i !! counter of characters in `path` integer(IK) :: length !! significant length of `path` integer(IK) :: child_i !! index for getting children character(kind=CK,len=1) :: c !! a character in the `path` logical(LK) :: array !! flag when searching for array index in `path` type(json_value),pointer :: tmp !! temp variables for getting child objects logical(LK) :: child_found !! if the child value was found logical(LK) :: create !! if the object is to be created logical(LK) :: created !! if `create` is true, then this will be !! true if the leaf object had to be created integer(IK) :: j !! counter of children when creating object logical(LK) :: status_ok !! integer to string conversion flag nullify(p) if (.not. json%exception_thrown) then if (present(create_it)) then create = create_it else create = .false. end if ! default to assuming relative to me p => me child_i = 1 array = .false. created = .false. !keep trailing space or not: if (json%trailing_spaces_significant) then length = len(path) else length = len_trim(path) end if do i=1, length c = path(i:i) select case (c) case (root) ! root do while (associated (p%parent)) p => p%parent end do child_i = i + 1 if (create) created = .false. ! should always exist case (this) ! this p => me child_i = i + 1 if (create) created = .false. ! should always exist case (start_array,start_array_alt) ! start looking for the array element index array = .true. ! get child member from p if (child_i < i) then nullify(tmp) if (create) then ! Example: ! 'aaa.bbb(1)' ! -> and aaa is a null, need to make it an object ! ! What about the case: aaa.bbb(1)(3) ? ! Is that already handled? if (p%var_type==json_null) then ! (**) ! if p was also created, then we need to ! convert it into an object here: p%var_type = json_object end if ! don't want to throw exceptions in this case call json%get_child(p, path(child_i:i-1), tmp, child_found) if (.not. child_found) then ! have to create this child ! [make it an array] call json_value_create(tmp) call json%to_array(tmp,path(child_i:i-1)) call json%add(p,tmp) created = .true. else created = .false. end if else ! call the normal way call json%get_child(p, path(child_i:i-1), tmp) end if p => tmp else child_i = i + 1 ! say, '@(' cycle end if if (.not. associated(p)) then call json%throw_exception('Error in json_get_by_path_default:'//& ' Error getting array element',found) exit end if child_i = i + 1 case (end_array,end_array_alt) if (.not. array) then call json%throw_exception('Error in json_get_by_path_default:'//& ' Unexpected '//c,found) exit end if array = .false. call string_to_integer(path(child_i:i-1),child_i,status_ok) if (.not. status_ok) then call json%throw_exception('Error in json_get_by_path_default:'//& ' Could not convert array index to integer: '//& trim(path(child_i:i-1)),found) exit end if nullify(tmp) if (create) then ! don't want to throw exceptions in this case call json%get_child(p, child_i, tmp, child_found) if (.not. child_found) then if (p%var_type==json_null) then ! (**) ! if p was also created, then we need to ! convert it into an array here: p%var_type = json_array end if ! have to create this element ! [make it a null] ! (and any missing ones before it) do j = 1, child_i nullify(tmp) call json%get_child(p, j, tmp, child_found) if (.not. child_found) then call json_value_create(tmp) call json%to_null(tmp) ! array element doesn't need a name call json%add(p,tmp) if (j==child_i) created = .true. else if (j==child_i) created = .false. end if end do else created = .false. end if else ! call the normal way: call json%get_child(p, child_i, tmp) end if p => tmp child_i = i + 1 case default if (c==json%path_separator) then ! get child member from p if (child_i < i) then nullify(tmp) if (create) then if (p%var_type==json_null) then ! (**) ! if p was also created, then we need to ! convert it into an object here: p%var_type = json_object end if ! don't want to throw exceptions in this case call json%get_child(p, path(child_i:i-1), tmp, child_found) if (.not. child_found) then ! have to create this child ! [make it an object] call json_value_create(tmp) call json%to_object(tmp,path(child_i:i-1)) call json%add(p,tmp) created = .true. else created = .false. end if else ! call the normal way call json%get_child(p, path(child_i:i-1), tmp) end if p => tmp else child_i = i + 1 ! say '$.', '@.', or ').' cycle end if if (.not. associated(p)) then call json%throw_exception('Error in json_get_by_path_default:'//& ' Error getting child member.',found) exit end if child_i = i + 1 end if end select end do if (json%exception_thrown) then if (present(found)) then nullify(p) ! just in case found = .false. call json%clear_exceptions() end if else ! grab the last child if present in the path if (child_i <= length) then nullify(tmp) if (create) then if (p%var_type==json_null) then ! (**) ! if p was also created, then we need to ! convert it into an object here: p%var_type = json_object end if call json%get_child(p, path(child_i:i-1), tmp, child_found) if (.not. child_found) then ! have to create this child ! (make it a null since it is the leaf) call json_value_create(tmp) call json%to_null(tmp,path(child_i:i-1)) call json%add(p,tmp) created = .true. else created = .false. end if else ! call the normal way call json%get_child(p, path(child_i:i-1), tmp) end if p => tmp else ! we already have p if (create .and. created) then ! make leaf p a null, but only ! if it wasn't there call json%to_null(p) end if end if ! error checking if (associated(p)) then if (present(found)) found = .true. !everything seems to be ok else call json%throw_exception('Error in json_get_by_path_default:'//& ' variable not found: '//trim(path),found) if (present(found)) then found = .false. call json%clear_exceptions() end if end if end if ! if it had to be created: if (present(was_created)) was_created = created else if (present(found)) found = .false. if (present(was_created)) was_created = .false. end if end subroutine json_get_by_path_default