Skip to content

Fortran YAML Library

Reading a YAML File

Our objective is to read value fields parent_1 and parent_2 from the following YAML input.yaml file:

parent_1:  # a map
    child_a:
        boolean_flag: true
        an_aray: [0.999, 1.0]
        array_int_2d: [[1,   2,  3],[4,   5,  6],[7,   8,  9],[10, 11, 12]]
parent_2:  # a sequence
  - item: 3 x 3 matrix.
    value: [[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12]]
  - item: 4 x 3 matrix
    value: 
        item_is_float_array_2d: [[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12]]

Keys parent_1 ad parent_2 represent a map and a sequence, respectively. Shortly we will demonstrate a structure to read both items from the YAML file.

Read a Map

The syntax for reading a map is:

use YAMLRead
character(len=:), allocatable :: fpath
type(YAMLHandler) :: domain
type(YAMLMap) :: parent
type(YAMLMap) :: mapper
integer :: code = 0

fpath = "./input.yaml"

domain = yaml_open_file(fpath)
parent = yaml_start_from_map(domain, "parent_1")
mapper = parent%value_map("child_a")

write(*,*) "map values = ", mapper%value_str("boolean_flag", code)
write(*,*) "map values = ", mapper%value_double_1d("an_aray", code)
write(*,*) "map values = ", mapper%value_int_2d("array_int_2d", code)

call mapper%destroy()
call parent%destroy()
call yaml_close_file(domain)
deallocate(fpath)

After importing the YAMLRead module, the next step is to declare the variables we will use. A YAMLHandler instance stores the file pointer and two YAMLMap's are declared as entry points into the parent_1 and child_a maps. Each hierarchial level requires its own YAMLMap declaration in order to safely manage memory associated with those nodes. Therefore, a YAMLMap cannot be resued once it is assigned unless it is destroyed.

Next, we use the yaml_start_from_map function to retrieve the lead node to designate a location to start scouring the YAML file. Both parent_1 and parent_2 are valid lead nodes in the example file above. Now with the lead node set, we retrieve the child_a map through the value_map module procedure:

mapper = parent%value_map("child_a")

As demonstrated above, there's a different mechanism to access YAML nodes after the lead node is selected. Although it's debatable whether this is a good idea, this decoration exists in order to preserve a consistent access style we see with the YAML-cpp library. Each key value can be accessed using:

write(*,*) "mapper values = ", mapper%value_str("boolean_flag", code)
write(*,*) "mapper values = ", mapper%value_double_1d("an_aray", code)
write(*,*) "mapper values = ", mapper%value_int_2d("array_int_2d", code)

to print YAML content to the console. Objects are deleted in the reverse order they were allocated to release memory. The code argument is an exit code. If value extraction is successful then code == 0, otherwise it is another positive value.

Read a Sequence

Reading a sequence is similiar to maps:

use YAMLRead
character(len=:), allocatable :: fpath
type(YAMLHandler) :: domain
type(YAMLSequence) :: seq
type(YAMLElement) :: elem
integer :: n, i

fpath = "./input.yaml" !
domain = yaml_open_file(fpath)
seq = yaml_start_from_sequence(domain, "parent_2")

elem = seq%element(0)

write(*,*) "element 1 = ", elem%value_str("item", code)
write(*,*) "element 1 = ", elem%value_int_2d("value", code)

n = size(elem%labels)
do i = 1,n
  write(*,*) "  ", elem%labels(i)%str
end do

call elem%destroy()
call seq%destroy()
call yaml_close_file(domain)
deallocate(fpath)

One obvious difference is the delcaration of yaml_start_from_sequence to access the parent_2 lead node. This function call is necessary since parent_2 is a sequence. Another key difference is the declaration of YAMLSequence and YAMLElement types in order to access interior parts of the sequence.

Once variable seq is assigned, then interior elements can be accessed using the element(...) module procedure. Th inpur argument to this procedure is zero-indexed; thus if we wanted to access the second sequence entry, then the artgument is element(1). Sequence values are queried using the value_<> getters, a similiarity shared with maps. Resources are destroyed in the reverse order they are allocated.