JSON-FortranのAPIドキュメントを読むための解説

## 初めに

http://jacobwilliams.github.io/json-fortran/index.html

自力でこのAPIドキュメントからライブラリの使用方法を見つけ出し、コードを作成することを目指した解説になります。  

## 3つの派生型

基本的に`Derived Types`の箇所を見るだけで十分です。  

http://jacobwilliams.github.io/json-fortran/lists/types.html

こちらを見るとわかる通り3つの派生型(この場合は他言語ではclassに相当するもの)があり、この3つを利用してプログラムコードを作成します。  


### それぞれの派生型の内容

- `json_value`:データ構造の本体、json形式で格納されたデータをpointerを駆使してfortranのデータ構造に落とし込んだもの。親や子や自分自身の前後に対してpointerを張ってデータの構造を作成している。（ライブラリの利用者はこの内部のデータを直接読み取ることはできず、`json_core`を利用する必要がある。）
- `json_core`:出力する時の表示形式の設定以外の内部のデータをほぼ保持しない。関数的な操作で、`json_value`を直接加工したり、ファイルと`json_value`の相互変換や文字列型と`json_value`の相互変換を行う。  
- `json_file`:`json_value`と`json_core`がprivateで組み込まれており、ファイル読み込んで内部で`json_value`を組み立て、内部で`json_core`を利用している。

### 方針

jsonファイルを読み込む場合(一部例外あり)または`json_value`を作らなくても済むような簡単なjsonファイルを作成したい場合は`json_file`を利用する。  
jsonファイルを作成したい場合は`json_core`を利用する。  
データが構造を持つものは`json_value`を使用する。  

つまり、簡単な場合は`json_file`、複雑な場合は`json_core`と`json_value`を使う。

## 用語

- p: `json_value`のポインタ
- path: jsonの要素を指し示す名称　例：'inputs.flag'
- val: jsonの要素
- vec: jsonの要素のうち配列をさす

## json_valueのポインタの内容

http://jacobwilliams.github.io/json-fortran/type/json_value.html

こちらにある通り、各ポインタには自身の名前、データ、親のポインタ、１番目の子のポインタ情報が格納されている。  
下記の例で実際にポインタの状態を見てみる。  
ポイントはどれが親と子の関係なのか、名前のありなしである。

```json
{
  "id": 1,
  "name": "Banana",
  "price": 12.50,
  "tags": [ "home", "green" ],
  "list": [
    {
      "lon": 130,
      "lat": 34
    },
    {
      "lon": 131,
      "lat": 35
      }
  ]
}
```

この例だとrootは名前なしオブジェクト("")、rootの子供はすべて名前があり並列で`"id": 1`->`"name": "Banana"`->`"price": 12.50`->`"tags": [ "home", "green" ]`->`  "list": [
    {
      "lon": 130,
      "lat": 34
    },
    {
      "lon": 131,
      "lat": 35
      }
  ]`の順に並んでいる。  
`tags`と`home`は親子関係になっており、`tags`はtagsという名前を持った配列であり、`home`は名前をもたない文字列となる。
`list`と`{"lon": 130,"lat": 34}`は親子関係になっており、`list`はlistという名前を持った配列であり、`{"lon": 130,"lat": 34}`は名前を持たないオブジェクトになる。

## json_coreのType-Bound Proceduresの読み方

`Type-Bound Procedures`は直訳すると型に束縛されたプロシージャであるが、オブジェクト指向におけるオブジェクトのメソッドと思ってよい。  

### add Procedures を例に

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/998887/d2df3f39-e5ba-dd00-1d24-ac63a1b07fa7.png)

`generic, public :: add => ...`と書かれてあるので、総称名であり引数の型や要素数に応じて実行される中身が異なる。
総称名がpublic、個別名がprivateなので、総称名でのみしか実行できないようになっている。  
具体的に個別名`json_value_add_integer`プロシージャを見ると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/998887/be0eba9c-3c5c-fd60-2260-dddfe475c060.png)

引数が4つあるが、`Type-Bound Procedures`なので1つ目は派生型自身を指しており、引数として入力する際は`(p, name, val)`となる。
`json_value`型の`p`の子供に名前が`name`の整数値`val`を加えるという動作を行っている。

## 作成方法その１

ここでは派生型`json_core`を利用して派生型`json_value`のポインタの構築を行い、それをファイルに出力するまでのプログラムを構築する。

```fortran
program test
    use json_module
    implicit none
    type(json_core) :: json
    type(json_value), pointer :: root_p, list_p, tmp_p

    ! list_pの作成
    call json%create_array(list_p, 'list')
    call json%create_object(tmp_p, '')
    call json%add(tmp_p, 'lon', 130)
    call json%add(tmp_p, 'lat', 34)
    call json%add(list_p, tmp_p)
    call json%deserialize(tmp_p, '{"lon":131,"lat":35}') !　json文字列からjson_value型へ
    call json%add(list_p, tmp_p)

    ! root_pの作成
    call json%create_object(root_p, '')
    call json%add(root_p, "id", 1)
    call json%add(root_p, "name", "Banana")
    call json%add(root_p, "price", 12.50)
    call json%add(root_p, "tags", [character(len=5):: "home", "green"], trim_str=.true.) ! 文字列の配列も可
    call json%add(root_p, list_p)

    ! ファイルに出力
    call json%print(root_p, "file.json")

    ! ポインタに格納されたデータを消去
    call json%destroy(root_p)

end program test
```

### 方針

まずは`list`という名前の付いた配列の`json_value`型を作成する。
そのうえで、名前なしオブジェクト({})を作成し、その中に子供を付け加える。

### create系プロシージャ

```fortran
type(json_core) :: json
type(json_value), pointer :: root_p, test_p, list_p
call json%create_object(root_p, '') ! 名前なしオブジェクト
call json%create_object(test_p, 'test') ! 名前ありオブジェクト
call json%create_array(list_p, 'list') ! 配列
```

オブジェクトまたは配列のポインタを作成する。  
それ以外要素もcreate系プロシージャで作成できるようであるが、オブジェクトまたは配列の子供として存在するはずなので、add系プロシージャで付け加えればよい。  

### add系プロシージャ

```fortran
call json%add(root_p, "id", 1)
call json%add(root_p, list_p) !pointerにpointerを加える場合。
```

また上記の作成例のように`deserialize`プロシージャを用いればjson文字列から直接`json_value`に変換が可能になるので、簡単なものはそれで作成が可能である。  


## 作成方法その２

派生型`json_file`のみを利用して、jsonファイルに出力するまでのプログラムを構築する。

```fortran
program test
    use, intrinsic :: iso_fortran_env, only: int32
    use json_module
    implicit none
    type(json_file) :: json_f
    integer(int32) :: lon(2) = [130, 131]
    integer(int32) :: lat(2) = [34, 35]
    integer(int32) :: i
    character(1) :: num

    call json_f%initialize() ! 内部のjson_valueをリセット
    call json_f%add('id', 1)
    call json_f%add('name', 12.50)
    call json_f%add('tags', [character(len=5):: "home", "green"], trim_str=.true.)

    do i = 1, size(lon)
        write (num, '(I1)') i
        call json_f%add('list('//num//').lon', lon(i))
        call json_f%add('list('//num//').lat', lat(i))
    end do

    call json_f%print("file.json") ! ファイルに出力
    call json_f%destroy() ! 内部のjson_valueを破壊

end program test
```

### add系プロシージャ

```fortran
call json_f%add('list(1).lon', 134)
```

`path`か`json_value`での入力のみ。したがって`json_file`のみを利用する場合は`path`だけで簡単に構築できるかどうかが目安になる。  

### printプロシージャ

```fortran
call json_f%print() !コンソール出力
call json_f%print("file.json") !ファイル出力
call json_f%print(iunit=12) !file unit numberへの出力
```

引数の型に応じて出力先が変わる。

## 配列の中にオブジェクトが入っているjsonファイルを読み取る場合

配列の中にオブジェクトが入っているjsonファイル(下記のような例)の場合は`json_file`のみでの読みよりに苦労する。  
派生型`json_file`からは直接指定された`path`の子供の数の読み取りができないからである。  

```json:file.json
{
  "id": 1,
  "name": "Banana",
  "price": 12.50,
  "tags": [ "home", "green" ],
  "list": [
    {
      "lon": 130,
      "lat": 34
    },
    {
      "lon": 131,
      "lat": 35
      }
  ]
}
```

### 方針

`list`の要素数が決まっている場合は`path`で直接すれば済むので`json_file`のみで問題がない。  
`list`の要素数が決まっていない場合は以下の２通りで行う。  

1.  `json_file`でデータを取得して、`json_value`で出力可能なため、一部のデータを`json_value`で出力して`json_core`で読み取る。  
1. 最初から`json_core`を使う。  

### 1つ目の方法

```fortran
program test
    use, intrinsic :: iso_fortran_env, only: int32
    use json_module
    implicit none
    type(json_file) :: json_f
    type(json_core) :: json_c
    type(json_value), pointer :: p, p_list_child
    integer(int32), allocatable :: lon(:)
    integer(int32), allocatable :: lat(:)
    integer(int32) :: id
    integer(int32) :: num
    integer(int32) :: i

    call json_f%initialize() ! 内部のjson_valueをリセット
    call json_f%load('file.json')

    call json_f%get(path='id', val=id)
    call json_f%get(path="list", p=p) !ポインタに出力
    num = json_c%count(p=p)
    allocate (lon(num))
    allocate (lat(num))
    do i = 1, num
        call json_c%get_child(p=p, idx=i, child=p_list_child)
        call json_c%get(me=p_list_child, path="lon", value=lon(i))
        call json_c%get(me=p_list_child, path="lat", value=lat(i))
    end do
    print *, lon
    call json_f%destroy() ! 内部のjson_valueを破壊

end program test
```


### 2つ目の方法

```fortran
program test
    use, intrinsic :: iso_fortran_env, only: int32
    use json_module
    implicit none
    type(json_core) :: json_c
    type(json_value), pointer :: p, p_list, p_list_child
    integer(int32), allocatable :: lon(:)
    integer(int32), allocatable :: lat(:)
    integer(int32) :: id
    integer(int32) :: num
    integer(int32) :: i

    call json_c%load(file='file.json', p=p)
    call json_c%get(me=p, path='id', value=id)
    print *, id
    call json_c%get(me=p, path='list', p=p_list)

    num = json_c%count(p=p_list)
    allocate (lon(num))
    allocate (lat(num))
    do i = 1, num
        call json_c%get_child(p=p_list, idx=i, child=p_list_child)
        call json_c%get(me=p_list_child, path="lon", value=lon(i))
        call json_c%get(me=p_list_child, path="lat", value=lat(i))
    end do
    print *, lon, lat
    call json_c%destroy(p)

end program test
```


## 終わりに

エラー処理は特に書きませんでしたが必要に応じてAPIドキュメントを読んで書きましょう。