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.
The syntax is inherited from FSON, and is basically a subset of JSONPath "dot-notation", with the additional allowance of () for array elements.
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).
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
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')
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)
exit
end if
array = .false.
child_i = json%string_to_int(path(child_i:i-1))
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.')
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))
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