Previous topic

The yajl_fort module

This Page

The json module

The json module defines derived data types for representing arbitrary JSON data, and procedures for instantiating objects of those types from JSON text read from a file or string.

This module uses yajl_fort for parsing the JSON input data.

Note

This module is a work-in-progress. While it provides the ability to read arbitrary JSON data and represent it in memory, it lacks many convenient methods for working with the data. Needed in particular, are methods for direct access to values using a “path” type of indexing.

Usage

Refer to http://www.json.org for a detailed description of the JSON syntax. The derived types and terminology used here adhere closely to that description.

The abstract type json_value represents a JSON value. The dynamic type of a polymorphic instance of this class will be one of these extended types:

json_integer:

stores a JSON number without fractional part (P)

json_real:

stores a JSON number with fractional part (P)

json_string:

stores a JSON string (P)

json_boolean:

stores a logical for the JSON literals true and false (P)

json_null:

represents the JSON literal null (P)

json_object:

stores a JSON object (S)

json_array:

stores a JSON array (S)

The primitive types (P) have a public component %value that stores the corresponding value (except for json_null). The content of the structure types (S) are accessed via iterator objects. For json_object values:

type(json_object), target  :: value
type(json_object_iterator) :: iter
iter = json_object_iterator(value)
do while (.not.iter%at_end()) ! order of object members is insignificant
  ! iter%name() is the name of the member
  ! iter%value() is a class(json_value) pointer to the value of the member
  call iter%next
end do

For json_array values:

type(json_array), target  :: value
type(json_array_iterator) :: iter
iter = json_array_iterator(value)
do while (.not.iter%at_end()) ! order of array elements *is* significant
  ! iter%value() is a class(json_value) pointer to the value of the element
  call iter%next
end do

The following subroutines allocate and define an allocatable class(json_value) variable with JSON text read from a string or logical unit opened for unformatted stream input.

call json_from_string(string, value, stat, errmsg)
call json_from_stream(unit,   value, stat, errmsg)
  character(*), intent(in) :: string
  integer, intent(in) :: unit
  class(json_value), allocatable, intent(out) :: value
  integer, intent(out) :: stat
  character(:), allocatable, intent(out) :: errmsg

The argument stat returns a nonzero value if an error occurs, and in that case errmsg is assigned an explanatory error message.

Examples

Here are some examples that use json_from_string. Examples using json_from_stream would be essentially the same. Note that the stop statements identify things that should not occur.

Reading primitive JSON values:

use json

class(json_value), allocatable :: val
character(:), allocatable :: errmsg
integer :: stat

call json_from_string('42', val, stat, errmsg)
select type (val)
type is (json_integer)
  if (val%value /= 42) stop 1
class default
  stop 2
end select

call json_from_string('"foo"', val, stat, errmsg)
select type (val)
type is (json_string)
  if (val%value /= 'foo') stop 3
class default
  stop 4
end select

call json_from_string('false', val, stat, errmsg)
if (stat /= 0) stop 51
select type (val)
type is (json_boolean)
  if (val%value) stop 5
class default
  stop 6
end select

call json_from_string('null', val, stat, errmsg)
select type (val)
type is (json_null)
class default
  stop 7
end select

Reading a JSON array value and iterating through its elements:

use json

class(json_value), allocatable :: val
type(json_array_iterator) :: iter
character(:), allocatable :: errmsg
integer :: stat, n

call json_from_string('[42,"foo",false,null]', val, stat, errmsg)

select type (val)
type is (json_array)
  n = 0
  iter = json_array_iterator(val)
  do while (.not.iter%at_end())
    n = n + 1
    select type (ival => iter%value())
    type is (json_integer)
      if (n /= 1) stop 1
      if (ival%value /= 42) stop 2
    type is (json_string)
      if (n /= 2) stop 3
      if (ival%value /= 'foo') stop 4
    type is (json_boolean)
      if (n /= 4) stop 5
      if (ival%value) stop 6
    type is (json_null)
      if (n /= 5) stop 7
    class default
      stop 8
    end select
    call iter%next
  end do
class default
  stop 9
end select

Reading a JSON object value and iterating through its members:

use json

class(json_value), allocatable :: val
type(json_object_iterator) :: iter
character(:), allocatable :: errmsg
integer :: stat

call json_from_string('{"a":42,"b":"foo","c":false}', val, stat, errmsg)

select type (val)
type is (json_object)
  iter = json_object_iterator(val)
  do while (.not.iter%at_end())
    select type (ival => iter%value())
    type is (json_integer)
      if (iter%name() /= 'a') stop 1
      if (ival%value /= 42) stop 2
    type is (json_string)
      if (iter%name() /= 'b') stop 3
      if (ival%value /= 'foo') stop 4
    type is (json_boolean)
      if (iter%name() /= 'y') stop 6
      if (ival%value) stop 6
    class default
      stop 7
    end select
    call iter%next
  end do
class default
  stop 8
end select

Error handling with invalid JSON:

use json

class(json_value), allocatable :: val
integer :: stat
character(:), allocatable :: errmsg

call json_from_string('[1,2,foo,3]', val, stat, errmsg)
if (stat == 0) stop 1 ! should have been an error
write(*,*) errmsg

This produces this error output when run:

lexical error: invalid string in json text.
                                 [1,2,foo,3]
                    (right here) ------^