初めに
自力でこの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ドキュメントを読んで書きましょう。