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)
Note
Uses 1-based array indices (same as json_get_by_path_default, but unlike json_get_by_path_rfc6901 which uses 0-based indices).
Note
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.
Note
JSON null
values are used here for unknown variables
when create_it
is True.
Warning
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