初めに
自力でこのAPIドキュメントからライブラリの使用方法を見つけ出し、コードを作成することを目指した解説になります。
3つの派生型
基本的にDerived Typesの箇所を見るだけで十分です。
こちらを見るとわかる通り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のポインタの内容
こちらにある通り、各ポインタには自身の名前、データ、親のポインタ、1番目の子のポインタ情報が格納されている。
下記の例で実際にポインタの状態を見てみる。
ポイントはどれが親と子の関係なのか、名前のありなしである。
{
"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 を例に
generic, public :: add => ...と書かれてあるので、総称名であり引数の型や要素数に応じて実行される中身が異なる。
総称名がpublic、個別名がprivateなので、総称名でのみしか実行できないようになっている。
具体的に個別名json_value_add_integerプロシージャを見ると
引数が4つあるが、Type-Bound Proceduresなので1つ目は派生型自身を指しており、引数として入力する際は(p, name, val)となる。
json_value型のpの子供に名前がnameの整数値valを加えるという動作を行っている。
作成方法その1
ここでは派生型json_coreを利用して派生型json_valueのポインタの構築を行い、それをファイルに出力するまでのプログラムを構築する。
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系プロシージャ
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系プロシージャ
call json%add(root_p, "id", 1)
call json%add(root_p, list_p) !pointerにpointerを加える場合。
また上記の作成例のようにdeserializeプロシージャを用いればjson文字列から直接json_valueに変換が可能になるので、簡単なものはそれで作成が可能である。
作成方法その2
派生型json_fileのみを利用して、jsonファイルに出力するまでのプログラムを構築する。
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系プロシージャ
call json_f%add('list(1).lon', 134)
pathかjson_valueでの入力のみ。したがってjson_fileのみを利用する場合はpathだけで簡単に構築できるかどうかが目安になる。
printプロシージャ
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の子供の数の読み取りができないからである。
{
"id": 1,
"name": "Banana",
"price": 12.50,
"tags": [ "home", "green" ],
"list": [
{
"lon": 130,
"lat": 34
},
{
"lon": 131,
"lat": 35
}
]
}
方針
listの要素数が決まっている場合はpathで直接すれば済むのでjson_fileのみで問題がない。
listの要素数が決まっていない場合は以下の2通りで行う。
-
json_fileでデータを取得して、json_valueで出力可能なため、一部のデータをjson_valueで出力してjson_coreで読み取る。 - 最初から
json_coreを使う。
1つ目の方法
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つ目の方法
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ドキュメントを読んで書きましょう。

