Returns the json_value pointer given the path string, using the “JSON Pointer” path specification defined by the JSONPath “bracket-notation”.
The first character $
is optional, and signifies the root
of the structure. If it is not present, then the first key
is taken to be in the me
object.
Single or real quotes may be used.
type(json_core) :: json
type(json_value),pointer :: dat,p
logical :: found
!...
call json%initialize(path_mode=3)
call json%get(dat,"$['store']['book'][1]['title']",p,found)
Uses 1-based array indices (same as json_get_by_path_default, but unlike json_get_by_path_rfc6901 which uses 0-based indices).
When create_it=True
, if the variable already exists and is a type
that is not compatible with the usage in the path
, then it is
destroyed and replaced with what is specified in the path
. Note that
this applies the all variables in the path as it is created. Currently,
this behavior is different from json_get_by_path_default.
JSON null
values are used here for unknown variables
when create_it
is True.
Note that if using single quotes, this routine cannot parse
a key containing ']
. If using real quotes, this routine
cannot parse a key containing "]
. If the key contains both
']
and "]
, there is no way to parse it using this routine.
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 (using JSONPath “bracket-notation”) |
||
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_jsonpath_bracket(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
!! (using JSONPath
!! "bracket-notation")
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.
character(kind=CK,len=:),allocatable :: token !! a token in the path
!! (between the `['']` or
!! `[]` characters)
integer(IK) :: istart !! location of current '['
!! character in the path
integer(IK) :: iend !! location of current ']'
!! character in the path
integer(IK) :: ival !! integer array index value
logical(LK) :: status_ok !! error flag
type(json_value),pointer :: tmp !! temporary variable for
!! traversing the structure
integer(IK) :: i !! counter
integer(IK) :: ilen !! length of `path` string
logical(LK) :: real_quotes !! if the keys are enclosed in `"`,
!! rather than `'` tokens.
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
!TODO instead of reallocating `token` all the time, just
! allocate a big size and keep track of the length,
! then just reallocate only if necessary.
! [would probably be inefficient if there was a very large token,
! and then a bunch of small ones... but for similarly-sized ones
! it should be way more efficient since it would avoid most
! reallocations.]
nullify(p)
if (.not. json%exception_thrown) then
if (present(create_it)) then
create = create_it
else
create = .false.
end if
p => me ! initialize
created = .false.
if (path==CK_'') then
call json%throw_exception('Error in json_get_by_path_jsonpath_bracket: '//&
'invalid path specification: '//trim(path),found)
else
if (path(1:1)==root .or. path(1:1)==start_array) then ! the first character must be
! a `$` (root) or a `[`
! (element of `me`)
if (path(1:1)==root) then
! go to the root
do while (associated (p%parent))
p => p%parent
end do
if (create) created = .false. ! should always exist
end if
!path length (don't need trailing spaces:)
ilen = len_trim(path)
if (ilen>1) then
istart = 2 ! initialize first '[' location index
do
if (istart>ilen) exit ! finished
! must be the next start bracket:
if (path(istart:istart) /= start_array) then
call json%throw_exception(&
'Error in json_get_by_path_jsonpath_bracket: '//&
'expecting "[", found: "'//trim(path(istart:istart))//&
'" in path: '//trim(path),found)
exit
end if
! get the next token by checking:
!
! * [''] -- is the token after istart a quote?
! if so, then search for the next `']`
!
! * [1] -- if not, then maybe it is a number,
! so search for the next `]`
! verify length of remaining string
if (istart+2<=ilen) then
real_quotes = path(istart+1:istart+1) == quotation_mark ! ["
if (real_quotes .or. path(istart+1:istart+1)==single_quote) then ! ['
! it might be a key value: ['abc']
istart = istart + 1 ! move counter to ' index
if (real_quotes) then
iend = istart + index(path(istart+1:ilen),&
quotation_mark//end_array) ! "]
else
iend = istart + index(path(istart+1:ilen),&
single_quote//end_array) ! ']
end if
if (iend>istart) then
! istart iend
! | |
! ['p']['abcdefg']
if (iend>istart+1) then
token = path(istart+1:iend-1)
else
token = CK_'' ! blank string
end if
! remove trailing spaces in
! the token here if necessary:
if (.not. json%trailing_spaces_significant) &
token = trim(token)
if (create) then
! have a token, create it if necessary
! we need to convert it into an object here
! (e.g., if p was also just created)
! and destroy its data to prevent a memory leak
call json%convert(p,json_object)
! don't want to throw exceptions in this case
call json%get_child(p,token,tmp,status_ok)
if (.not. status_ok) then
! have to create this child
! [make it a null since we don't
! know what it is yet]
call json_value_create(tmp)
call json%to_null(tmp,token)
call json%add(p,tmp)
status_ok = .true.
created = .true.
else
! it was already there.
created = .false.
end if
else
! have a token, see if it is valid:
call json%get_child(p,token,tmp,status_ok)
end if
if (status_ok) then
! it was found
p => tmp
else
call json%throw_exception(&
'Error in json_get_by_path_jsonpath_bracket: '//&
'invalid token found: "'//token//&
'" in path: '//trim(path),found)
exit
end if
iend = iend + 1 ! move counter to ] index
else
call json%throw_exception(&
'Error in json_get_by_path_jsonpath_bracket: '//&
'invalid path: '//trim(path),found)
exit
end if
else
! it might be an integer value: [123]
iend = istart + index(path(istart+1:ilen),end_array) ! ]
if (iend>istart+1) then
! this should be an integer:
token = path(istart+1:iend-1)
! verify that there are no spaces or other
! characters in the string:
status_ok = .true.
do i=1,len(token)
! It must only contain (0..9) characters
! (it must be unsigned)
if (scan(token(i:i),CK_'0123456789')<1) then
status_ok = .false.
exit
end if
end do
if (status_ok) then
call string_to_integer(token,ival,status_ok)
if (status_ok) status_ok = ival>0 ! assuming 1-based array indices
end if
if (status_ok) then
! have a valid integer to use as an index
! see if this element is really there:
call json%get_child(p,ival,tmp,status_ok)
if (create .and. .not. status_ok) then
! have to create it:
if (.not.(p%var_type==json_object .or. p%var_type==json_array)) then
! we need to convert it into an array here
! (e.g., if p was also just created)
! and destroy its data to prevent a memory leak
call json%convert(p,json_array)
end if
! have to create this element
! [make it a null]
! (and any missing ones before it)
do j = 1, ival
nullify(tmp)
call json%get_child(p, j, tmp, status_ok)
if (.not. status_ok) 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==ival) created = .true.
else
if (j==ival) created = .false.
end if
end do
status_ok = .true.
else
created = .false.
end if
if (status_ok) then
! found it
p => tmp
else
! not found
call json%throw_exception(&
'Error in json_get_by_path_jsonpath_bracket: '//&
'invalid array index found: "'//token//&
'" in path: '//trim(path),found)
exit
end if
else
call json%throw_exception(&
'Error in json_get_by_path_jsonpath_bracket: '//&
'invalid token: "'//token//&
'" in path: '//trim(path),found)
exit
end if
else
call json%throw_exception(&
'Error in json_get_by_path_jsonpath_bracket: '//&
'invalid path: '//trim(path),found)
exit
end if
end if
else
call json%throw_exception(&
'Error in json_get_by_path_jsonpath_bracket: '//&
'invalid path: '//trim(path),found)
exit
end if
! set up for next token:
istart = iend + 1
end do
end if
else
call json%throw_exception(&
'Error in json_get_by_path_jsonpath_bracket: '//&
'expecting "'//root//'", found: "'//path(1:1)//&
'" in path: '//trim(path),found)
end if
end if
if (json%exception_thrown) then
nullify(p)
if (present(found)) then
found = .false.
call json%clear_exceptions()
end if
else
if (present(found)) found = .true.
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_jsonpath_bracket