今回は、ObjectScriptのオブジェクト(oref)とJSONを紐づける%JSON.Adaptorについて解説します。
はじめに
JSONはテキストベースのデータフォーマットで、構造がとてもシンプルです。
また、直観的な操作と広範な言語サポートにより、APIやデータ交換において非常に効率的なフォーマットとして認識されています。
%JSON.Adaptorを使用する事で、ObjectScriptのデータをJSON形式に変換するのがとても容易になります。
今回は、orefのデータとJSONの連携を行う、%JSON.Adaptorについて解説致します。
サンプルは全てIRIS 2023.1.2.450.0で作成しています。
使用方法
クラス定義
「%JSON.Adaptor」を継承したクラスを定義します。
Class developer.json.TestData Extends (%Persistent, %JSON.Adaptor)
{
Index prim On PatientId [ PrimaryKey ];
Property PatientId As %String(CAPTION = "患者ID");
Property Name As %String(CAPTION = "漢字氏名");
Property KanaName As %String(CAPTION = "カナ氏名");
Property Sex As %Integer(CAPTION = "性別", DISPLAYLIST = ",男性,女性,不明", VALUELIST = ",1,2,3");
Property BirthDay As %Date(CAPTION = "生年月日");
}
JSONの生成
JSONテキスト取得
データの登録前と登録後でJSON(ダイナミック・エンティティ)を生成したサンプルを作成しました。
orefに対し「%JSONExport」を実行するだけで、JSONテキストの取得が可能です。
ClassMethod makeJSON()
{
d ##class(developer.json.TestData).%KillExtent() // データの削除
s obj = ##class(developer.json.TestData).%New()
, obj.PatientId = "000001"
, obj.Name = "藤原 道長"
, obj.KanaName = "フジワラ ミチナガ"
, obj.Sex = 1
, obj.BirthDay = $zdh("1900-01-01",3)
s txt = obj.%JSONExport() // 登録前のJSONテキスト確認
w txt,!
d obj.%Save() // データ登録
s id = obj.%Id()
s new = ##class(developer.json.TestData).%OpenId(id)
d new.%JSONExport() // 登録後のJSONテキスト確認
}
【関数の実行結果】
{“PatientId”:”000001″,”Name”:”藤原 道長”,”KanaName”:”フジワラ ミチナガ”,”Sex”:1,”Birt hDay”:”1900-01-01″}
{“PatientId”:”000001″,”Name”:”藤原 道長”,”KanaName”:”フジワラ ミチナガ”,”Sex”:1,”Birt hDay”:”1900-01-01″}
変数にJSONテキストで取得
戻り値でstatusが取得できるので、JSONテキストの成功可否を判定可能
ClassMethod makeJSON()
{
s obj = ##class(developer.json.TestData).%New()
, obj.PatientId = "000001"
, obj.Name = "藤原 道長"
, obj.KanaName = "フジワラ ミチナガ"
, obj.Sex = 1
, obj.BirthDay = $zdh("1900-01-01",3)
s sts = obj.%JSONExportToString(.jsonTxt)
w jsonTxt
}
変数にJSONテキストをストリームで取得
JSONテキストをストリームで取得します。
ClassMethod makeJSON()
{
s obj = ##class(developer.json.TestData).%New()
, obj.PatientId = "000001"
, obj.Name = "藤原 道長"
, obj.KanaName = "フジワラ ミチナガ"
, obj.Sex = 1
, obj.BirthDay = $zdh("1900-01-01",3)
s sts = obj.%JSONExportToStream(.strm)
w strm,!
w strm.Read()
}
【関数の実行結果】
1@%Library.FileCharacterStream
{“PatientId”:”000001″,”Name”:”藤原 道長”,”KanaName”:”フジワラ ミチナガ”,”Sex”:1,”Birt hDay”:”1900-01-01″}
JSONからorefへ変換
「ストリーム」と「JSON(ダイナミック・エンティティ)」の両方から各プロパティにデータを充てることが可能です。
ClassMethod makeJSON()
{
s json = {
"PatientId":"000001",
"Name":"藤原 道長",
"KanaName":"フジワラ ミチナガ゙",
"Sex":1,
"BirthDay":($zdh("1900-01-01",3))
}
s strm = ##class(%Stream.GlobalCharacter).%New()
d json.%ToJSON(strm)
// ストリームからの変換
s new = ##class(developer.json.TestData).%New()
d new.%JSONImport(strm)
w new.Name,!
// json(ダイナミック・エンティティ)からの変換
s json = {
"PatientId":"000002",
"Name":"紫式部",
"KanaName":"ムラサキシキブ",
"Sex":2,
"BirthDay":($zdh("1901-12-31",3))
}
s oref = ##class(developer.json.TestData).%New()
d oref.%JSONImport(json)
w oref.Name
}
【関数の実行結果】
藤原 道長
紫式部
特殊ケース
特殊なデータ型のJSON化
リレーションを貼ったorefがJSON化した場合、どのような構造になるか確認してみます。
【親】
Class developer.json.TestData Extends (%Persistent, %JSON.Adaptor)
{
Index prim On PatientId [ PrimaryKey ];
Property PatientId As %String(CAPTION = "患者ID");
Property Name As %String(CAPTION = "漢字氏名");
Property Wife As list Of %List(CAPTION = "妻");
Property Tale As %List(CAPTION = "物語");
Property Comment As %Stream.GlobalCharacter(CAPTION = "コメント");
Property Seria As developer.json.ChildData2;
Property SeriaList As list Of developer.json.ChildData2;
Relationship Child As developer.json.ChildData [ Cardinality = children, Inverse = Parent ];}
【子(リレーション用)】
Class developer.json.ChildData Extends (%Persistent, %JSON.Adaptor)
{
Index prim On (Parent, Name) [ PrimaryKey ];
Relationship Parent As developer.json.TestData [ Cardinality = parent, Inverse = Child ];
Property Name As %String;
Property Sex As %Integer(CAPTION = "性別", DISPLAYLIST = ",男性,女性,不明", VALUELIST = ",1,2,3");
}
【子(シリアル用)】
Class developer.json.ChildData2 Extends (%SerialObject, %JSON.Adaptor)
{
Index prim On Name [ PrimaryKey ];
Property Name As %String;
Property Sex As %Integer(CAPTION = "性別", DISPLAYLIST = ",男性,女性,不明", VALUELIST = ",1,2,3");
}
【サンプルPG】
ClassMethod makeJSON()
{
d ##class(developer.json.TestData).%KillExtent()
s oref = ##class(developer.json.TestData).%New()
, oref.PatientId = "000001"
, oref.Name = "藤原 道長"
// $Listの配列
d oref.Wife.Insert($lb("源", "倫子"))
d oref.Wife.Insert($lb("源", "明子"))
// $List
s oref.Tale = $lb("源氏物語","紫式部")
d oref.Comment.Write("コメント")
// リレーション
s cObj = ##class(developer.json.ChildData).%New()
, cObj.Name = "藤原 頼通"
, cObj.Sex = 1
d oref.Child.Insert(cObj)
s cObj = ##class(developer.json.ChildData).%New()
, cObj.Name = "藤原 彰子"
, cObj.Sex = 2
d oref.Child.Insert(cObj)
// シリアル
s cObj = ##class(developer.json.ChildData2).%New()
, cObj.Name = "藤原 顕信"
, cObj.Sex = 1
s oref.Seria = cObj
// シリアル複数
s cObj = ##class(developer.json.ChildData2).%New()
, cObj.Name = "藤原 教通"
, cObj.Sex = 1
d oref.SeriaList.Insert(cObj)
s cObj = ##class(developer.json.ChildData2).%New()
, cObj.Name = "藤原 威子"
, cObj.Sex = 2
d oref.SeriaList.Insert(cObj)
// JSONテキスト取得
d oref.%JSONExportToString(.jsonTxt)
w jsonTxt,!!
// ダイナミック・エンティティ取得
s json = {}.%FromJSON(jsonTxt)
s new = ##class(developer.json.TestData).%New()
d new.%JSONImport(json)
d new.%JSONExportToString(.newText)
w newText,!
}
【関数の実行結果】
{“PatientId”:”000001″,”Name”:”藤原 道長”,”Wife”:[“源,倫子”,”源,明子”],”Tale”:” 源氏物語,紫式部”,”Comment”:”コメント”,”Seria”:{“Name”:”藤原 顕信”,”Sex”:1},”Ser iaList”:[{“Name”:”藤原 教通”,”Sex”:1},{“Name”:”藤原 威子”,”Sex”:2}],”Child”:[{ “Name”:”藤原 頼通”,”Sex”:1},{“Name”:”藤原 彰子”,”Sex”:2}]}
{
”PatientId”:”000001″,
”Name”:”藤原 道長”,
”Wife”:[
”源,倫子”,
”源,明子”
],
”Tale”:”源氏物語,紫式部”,
”Comment”:”コメント”,
”Seria”:{
”Name”:”藤原 顕信”,
”Sex”:1
},
”SeriaList”:[
{
”Name”:”藤原 教通”,
”Sex”:1
},
{
”Name”:”藤原 威子”,
”Sex”:2
}
],
”Child”:[
{
”Name”:”藤原 頼通”,
”Sex”:1
},
{
”Name”:”藤原 彰子”,
”Sex”:2
}
]
}
出力された結果より、下記が確認できます。
- リレーションとシリアライズは、JSONの構成が同じ仕様となる
- リレーションやシリアライズされた子は、配列(%DynamicArray)の子として構築される
- $Listは、$listToStringを実施した状態で構築される
- list of 型を指定すると、配列(%DynamicArray)として構築される
パラメータを使用してマッピングする
外部APIとの連携時、JSONのパラメータとプロパティ名が一致しないケースが想定できます。
そのような場合の回避策が、パラメータ「%JSONFIELDNAME」を利用したマッピングです。
【マッピング対象のクラス】
Class developer.json.ChangeName Extends (%RegisteredObject, %JSON.Adaptor)
{
/// プロパティ変換メソッドの生成可否
Parameter %JSONENABLED = 1;
/// 空値の処理 1:"", 0:$c(0), null
Parameter %JSONIGNORENULL = 0;
/// 未指定のプロパティの対応
Parameter %JSONNULL = 1;
/// オブジェクトの表現 OBJECT, ID, OID, GUID
Parameter %JSONREFERENCE = "OID";
/// 予期しないフィールドをエラーにする
Parameter %JSONIGNOREINVALIDFIELD = 0;
Property PatientId As %String(%JSONFIELDNAME = "bango");
Property Name As %String(%JSONFIELDNAME = "shimei");
/// Property Sex As %String(%JSONFIELDNAME = "seibetsu", %JSONINCLUDE = "inout");
Property Sex As %String(%JSONFIELDNAME = "seibetsu", %JSONINCLUDE = "inputonly");
Property BirthDay As %Date(%JSONFIELDNAME = "tanjobi", %JSONINCLUDE = "outputonly");
Property Secret As %String(%JSONINCLUDE = "none");
Property Job As %String(%JSONFIELDNAME = "yakushoku");
Property int As %Integer;
/// オブジェクトの確認
Property Oref As developer.data.Patient2;
}
【サンプルコマンド】
ClassMethod covName()
{
s json = {
"Date":"test",
"bango": "000001",
"shimei":"藤原 道長",
"seibetsu": "男",
"tanjobi" : ($zdh("1900-01-01",3)),
"Secret": "秘密"
}
s oref = ##class(developer.json.ChangeName).%New()
, oref.Oref = ##class(developer.data.Patient2).%OpenId(1)
// Import
s sts = oref.%JSONImport(json)
w oref.Sex,! // 性別の確認
w $system.Status.DisplayError(sts),!!
d oref.%JSONExportToString(.jtxt)
w jtxt,!!
// Export
s oref.BirthDay = $zdh("1900-01-01",3)
, oref.Secret = "秘密"
d oref.%JSONExportToString(.jtxt)
w jtxt,!
}
【関数の実行結果】
男
エラー #9404: class base マッピングを使用して予期しないフィールドの入力 Date です。1
{“bango”:”000001″,”shimei”:”藤原 道長”,”tanjobi”:null,”yakushoku”:null,”int”:nu ll,”Oref”:”developer.data.Patient2,1″}
{“bango”:”000001″,”shimei”:”藤原 道長”,”tanjobi”:”1900-01-01″,”yakushoku”:null, “int”:null,”Oref”:”developer.data.Patient2,1″}
解説
先ず抑えるポイントは下記2点で、プロパティのみに設定が可能です。
パラメータ | 説明 |
---|---|
%JSONFIELDNAME | プロパティ名とは別のフィールド名で、値の入出力を行う |
%JSONINCLUDE | マッピングの入出力時に制限を行う 入力時のみ:inputOnly、出力時のみ:outputOnly、両方:none |
後は全体 or 個別で制御を行う設定が下記になります。
パラメータ | 説明 |
---|---|
%JSONENABLED | 0を設定すると入出力が行えなくなる(初期値:1) |
%JSONIGNORENULL | 1を設定すると出力時「””」となる(初期値:0) |
%JSONNULL | 未指定のプロパティを出力する(初期値:0) |
%JSONREFERENCE | objectの出力設定[object, ID, OID, GUID](初期値:object) |
%JSONIGNOREINVALIDFIELD | 0:入力時に予期しないフィールドをエラーとする(初期値:0) |
XDataを使用してマッピングする
PropertyやParameterにマッピング情報を書き込むと、可読性が下がる事に懸念がある場合は、XDataにマッピング情報を設定する方法があります。
このマッピング方法であれば、データクラスの構造とマッピングを切り離す事が可能です。
また、XDataにマッピングしていないプロパティは、入出力を行わない特徴があります。
【マッピング対象のクラス】
Class developer.json.ChangeXData Extends (%RegisteredObject, %JSON.Adaptor)
{
Property PatientId As %String;
Property Name As %String;
Property Sex As %String;
Property BirthDay As %Date;
Property Secret As %String;
Property Job As %String;
Property int As %Integer;
/// オブジェクトの確認
Property Oref As developer.data.Patient2;
XData ChangeName
{
<Mapping xmlns="http://www.intersystems.com/jsonmapping" Null="1" IgnoreInvalidField="0" IgnoreNull="0" Reference="OID">
<Property Name="PatientId" FieldName="bango" />
<Property Name="Name" FieldName="shimei" />
<Property Name="Sex" FieldName="seibetsu" Include="inputonly" />
<Property Name="BirthDay" FieldName="tanjobi" Include="outputonly" />
<Property Name="Secret" Include="NONE" />
<Property Name="Job" FieldName="yakushoku" />
<Property Name="int" />
<Property Name="Oref" />
</Mapping>
}
}
【サンプルコマンド】
ClassMethod covNameXData()
{
s json = {
"Date":"test",
"bango": "000001",
"shimei":"藤原 道長",
"seibetsu": "男",
"tanjobi" : ($zdh("1900-01-01",3)),
"Secret": "秘密"
}
s oref = ##class(developer.json.ChangeXData).%New()
, oref.Oref = ##class(developer.data.Patient2).%OpenId(1)
// Import
s sts = oref.%JSONImport(json, "ChangeName")
w oref.Sex,!
w $system.Status.DisplayError(sts),!
d oref.%JSONExportToString(.jtxt, "ChangeName")
w jtxt,!
// Export
s oref.BirthDay = $zdh("1900-01-01",3)
, oref.Secret = "秘密"
d oref.%JSONExportToString(.jtxt, "ChangeName")
w jtxt,!
}
【関数の実行結果】
男
エラー #9404: class base マッピングを使用して予期しないフィールドの入力 Date です。1
{“bango”:”000001″,”shimei”:”藤原 道長”,”tanjobi”:null,”yakushoku”:null,”int”:nu ll,”Oref”:”developer.data.Patient2,1″}
{“bango”:”000001″,”shimei”:”藤原 道長”,”tanjobi”:”1900-01-01″,”yakushoku”:null, “int”:null,”Oref”:”developer.data.Patient2,1″}
各設定パラメータに関しては、「%JSON」を付与した元の名前に沿っています。
例)Null → %JSONNULL
FieldName → %JSONFIELDNAME
おわりに
%JSON.Adaptorは、単なるJSON変換ツールではなく、柔軟で高度な機能を備えています。
プロパティマッピングやXDataマッピング等を活用する事で、API連携がより効率的なものになります。
本記事で紹介した内容を活用し、快適なJSONライフを堪能してください。