@@ -510,6 +510,11 @@ module json_value_module
510510 procedure :: json_value_insert_after
511511 procedure :: json_value_insert_after_child_by_index
512512
513+ ! >
514+ ! get the path to a JSON variable in a structure:
515+ generic,public :: get_path = > MAYBEWRAP(json_get_path)
516+ procedure :: MAYBEWRAP(json_get_path)
517+
513518 procedure ,public :: remove = > json_value_remove ! ! Remove a [[json_value]] from a linked-list structure.
514519 procedure ,public :: check_for_errors = > json_check_for_errors ! ! check for error and get error message
515520 procedure ,public :: clear_exceptions = > json_clear_exceptions ! ! clear exceptions
@@ -4031,6 +4036,201 @@ subroutine wrap_json_get_by_path(json, me, path, p, found)
40314036 end subroutine wrap_json_get_by_path
40324037! *****************************************************************************************
40334038
4039+ ! *****************************************************************************************
4040+ ! >
4041+ ! Returns the path to a JSON object that is part
4042+ ! of a linked list structure.
4043+ !
4044+ ! The path returned would be suitable for input to
4045+ ! [[json_get_by_path]] and related routines.
4046+ !
4047+ ! @note If an error occurs (which in this case means a malformed
4048+ ! JSON structure) then an exception will be thrown, unless
4049+ ! `found` is present, which will be set to `false`. `path`
4050+ ! will be a blank string.
4051+
4052+ subroutine json_get_path (json , p , path , found , use_alt_array_tokens , path_sep )
4053+
4054+ implicit none
4055+
4056+ class(json_core),intent (inout ) :: json
4057+ type (json_value),pointer ,intent (in ) :: p ! ! a JSON linked list object
4058+ character (kind= CK,len= :),allocatable ,intent (out ) :: path ! ! path to the variable
4059+ logical (LK),intent (out ),optional :: found ! ! true if there were no problems
4060+ logical (LK),intent (in ),optional :: use_alt_array_tokens ! ! if true, then '()' are used for array elements
4061+ ! ! otherwise, '[]' are used [default]
4062+ character (kind= CK,len= 1 ),intent (in ),optional :: path_sep ! ! character to use for path separator
4063+ ! ! (default is '.')
4064+
4065+ type (json_value),pointer :: tmp ! ! for traversing the structure
4066+ type (json_value),pointer :: element ! ! for traversing the structure
4067+ integer (IK) :: var_type ! ! JSON variable type flag
4068+ character (kind= CK,len= :),allocatable :: name ! ! variable name
4069+ character (kind= CK,len= :),allocatable :: parent_name ! ! variable's parent name
4070+ character (kind= CK,len= max_integer_str_len) :: istr ! ! for integer to string conversion (array indices)
4071+ integer (IK) :: i ! ! counter
4072+ integer (IK) :: n_children ! ! number of children for parent
4073+ logical (LK) :: use_brackets ! ! to use '[]' characters for arrays
4074+ logical (LK) :: parent_is_root ! ! if the parent is the root
4075+
4076+ ! initialize:
4077+ path = ' '
4078+
4079+ ! optional input:
4080+ if (present (use_alt_array_tokens)) then
4081+ use_brackets = .not. use_alt_array_tokens
4082+ else
4083+ use_brackets = .true.
4084+ end if
4085+
4086+ if (associated (p)) then
4087+
4088+ ! traverse the structure via parents up to the root
4089+ tmp = > p
4090+ do
4091+
4092+ if (.not. associated (tmp)) exit ! finished
4093+
4094+ ! get info about the current variable:
4095+ call json% info(tmp,name= name)
4096+
4097+ ! if tmp a child of an object, or an element of an array
4098+ if (associated (tmp% parent)) then
4099+
4100+ ! get info about the parent:
4101+ call json% info(tmp% parent,var_type= var_type,&
4102+ n_children= n_children,name= parent_name)
4103+
4104+ select case (var_type)
4105+ case (json_array)
4106+
4107+ ! get array index of this element:
4108+ element = > tmp% parent% children
4109+ do i = 1 , n_children
4110+ if (.not. associated (element)) then
4111+ call json% throw_exception(' Error in json_get_path: ' // &
4112+ ' malformed JSON structure. ' )
4113+ exit
4114+ end if
4115+ if (associated (element,tmp)) then
4116+ exit
4117+ else
4118+ element = > element% next
4119+ end if
4120+ if (i== n_children) then ! it wasn't found (should never happen)
4121+ call json% throw_exception(' Error in json_get_path: ' // &
4122+ ' malformed JSON structure. ' )
4123+ exit
4124+ end if
4125+ end do
4126+ call integer_to_string(i,int_fmt,istr)
4127+ if (use_brackets) then
4128+ call add_to_path(parent_name// start_array// &
4129+ trim (adjustl (istr))// end_array,path_sep)
4130+ else
4131+ call add_to_path(parent_name// start_array_alt// &
4132+ trim (adjustl (istr))// end_array_alt,path_sep)
4133+ end if
4134+ tmp = > tmp% parent ! already added parent name
4135+
4136+ case (json_object)
4137+
4138+ ! process parent on the next pass
4139+ call add_to_path(name,path_sep)
4140+
4141+ case default
4142+
4143+ call json% throw_exception(' Error in json_get_path: ' // &
4144+ ' malformed JSON structure. ' // &
4145+ ' A variable that is not an object ' // &
4146+ ' or array should not have a child.' )
4147+ exit
4148+
4149+ end select
4150+
4151+ else
4152+ ! the last one:
4153+ call add_to_path(name,path_sep)
4154+ end if
4155+
4156+ if (associated (tmp% parent)) then
4157+ ! check if the parent is the root:
4158+ parent_is_root = (.not. associated (tmp% parent% parent))
4159+ if (parent_is_root) exit
4160+ end if
4161+
4162+ ! go to parent:
4163+ tmp = > tmp% parent
4164+
4165+ end do
4166+
4167+ else
4168+ call json% throw_exception(' Error in json_get_path: ' // &
4169+ ' input pointer is not associated' )
4170+ end if
4171+
4172+ ! for errors, return blank string:
4173+ if (json% exception_thrown) path = ' '
4174+
4175+ ! optional output:
4176+ if (present (found)) then
4177+ if (json% exception_thrown) then
4178+ found = .false.
4179+ call json% clear_exceptions()
4180+ else
4181+ found = .true.
4182+ end if
4183+ end if
4184+
4185+ contains
4186+
4187+ subroutine add_to_path (str ,dot )
4188+ ! ! prepend the string to the path
4189+ implicit none
4190+ character (kind= CK,len=* ),intent (in ) :: str ! ! string to prepend to `path`
4191+ character (kind= CK,len= 1 ),intent (in ),optional :: dot ! ! path separator (default is '.')
4192+ if (path==' ' ) then
4193+ path = str
4194+ else
4195+ if (present (dot)) then
4196+ path = str// dot// path
4197+ else
4198+ path = str// child// path
4199+ end if
4200+ end if
4201+ end subroutine add_to_path
4202+
4203+ end subroutine json_get_path
4204+ ! *****************************************************************************************
4205+
4206+ ! *****************************************************************************************
4207+ ! >
4208+ ! Wrapper for [[json_get_path]] where "path" and "path_sep" are kind=CDK.
4209+
4210+ subroutine wrap_json_get_path (json , p , path , found , use_alt_array_tokens , path_sep )
4211+
4212+ implicit none
4213+
4214+ class(json_core),intent (inout ) :: json
4215+ type (json_value),pointer ,intent (in ) :: p ! ! a JSON linked list object
4216+ character (kind= CDK,len= :),allocatable ,intent (out ) :: path ! ! path to the variable
4217+ logical (LK),intent (out ),optional :: found ! ! true if there were no problems
4218+ logical (LK),intent (in ),optional :: use_alt_array_tokens ! ! if true, then '()' are used for array elements
4219+ ! ! otherwise, '[]' are used [default]
4220+ character (kind= CDK,len= 1 ),intent (in ),optional :: path_sep ! ! character to use for path separator
4221+ ! ! (default is '.')
4222+
4223+ character (kind= CK,len= :),allocatable :: ck_path ! ! path to the variable
4224+
4225+ ! call the main routine:
4226+ call json_get_path(json,p,ck_path,found,use_alt_array_tokens,path_sep)
4227+
4228+ ! from unicode:
4229+ path = ck_path
4230+
4231+ end subroutine wrap_json_get_path
4232+ ! *****************************************************************************************
4233+
40344234! *****************************************************************************************
40354235! >
40364236! Convert a string into an integer.
0 commit comments