23 OCIでのJSONのサポート
SQL型JSONのサポートは、JSONデータ用に設計されています。Oracle DatabaseではJSONデータにJSON型を使用することをお薦めします。
この章には次のトピックが含まれます:
23.1 JSONデータ型のサポート
Oracle Databaseリリース20c以降では、SQL型JSONのサポートは、特にJSONデータ用に設計されています。Oracle DatabaseではJSONデータにJSON型を使用することをお薦めします。これにはバイナリ形式OSONが使用されます。これは、Oracle DatabaseサーバーとOracle Databaseクライアントの両方で問合せおよび更新を迅速に行うための、Oracle用に最適化されたバイナリJSON形式です。JSONデータ型を使用するには、compatible
データベース初期化パラメータに20以上を設定する必要があります。
JSONデータがSQLデータ型JSONの場合、Oracleでは標準のJSON言語のスカラー型(数値、文字列、ブール、nullなど)のセットが拡張され、SQLスカラー型に対応するスカラーが含まれています(binary、date、timestamp、year-month interval、day-second interval、double、floatなど)。これにより、JSON言語が拡張され、JSON言語とSQLの間でスカラー・データの変換がより簡単になり、データの損失がなくなります。
23.1.1 JSONのOCI表現
この項では、JSONのOCI表現について説明します。
標準JSONは、言語または表記法として、オブジェクト、配列、数値、文字列、ブール、nullなどのデータ型が事前定義されています。オブジェクトと配列を除くすべてのJSON言語型はスカラー型です。
OCIJson
記述子は、OCIのJSONドキュメントを表すために使用されます。これは記述子タイプOCI_DTYPE_JSON
で識別されます。この記述子は、OCIDescriptorAlloc()
関数およびOCIDescriptorFree ()
関数を使用して割当ておよび解放できます。ユーザーは、OCIArrayDescriptorAlloc()
関数およびOCIArrayDescriptorFree()
関数をそれぞれ使用して、記述子の配列の割当ておよび解放を行うことができます。
OCIJsonDomDocGet()
関数を使用すると、JsonDomDoc *
で表される基礎となるJSON DOM (Document Object Model)コンテナへの参照を取得できます。JSON DOMドキュメントには、オブジェクト型、配列型またはスカラー型のいずれかのノードが含まれています。
表23-1 型の作成および定数
プログラム変数 | 型定数 |
---|---|
JsonDomScalar |
JZNDOM_SCALAR |
JsonDomArray |
JZNDOM_ARRAY |
JsonDomObject |
JZNDOM_OBJECT |
注意:
JsonDomNode
は不完全な型です。前述の表に示されている型の値は、(JsonDomNode *)
型に設定できます。
23.2 可変DOMと不変DOM
この項では、JSON DOMの可変形式および不変形式について説明します。
OCI記述子を使用すると、可変DOMまたは不変DOMの2つの形式のJSON DOMのいずれかを取得できます。可変DOM内のDOMノードは変更できます。それに対して、不変DOMは読取り専用であり、DOMに対して書込み操作を行うとエラーが発生します。記述子によって保持されるデフォルトのDOMは、OCIAttrSet()
関数を使用してブール属性型に変更されないかぎり、不変です。
注意:
OCIJsonDomDocSet()
関数を使用して、記述子にJSON DOMコンテナを設定できます。ソースDOMは、記述子の形式と異なる形式にすることができます。つまり、ソースを可変DOMにして、ターゲット記述子に不変DOMを設定できます。
23.2.1 可変DOMとしてのJSONのマニフェスト
OCIクライアント・アプリケーションでは、JSONのコンテンツを可変DOMとしてマニフェストできます。XDKのC DOM関数を使用すると、JSONドキュメントとの間でJSON要素(オブジェクト、配列、スカラーなど)の読取りおよび書込みを行うことができます。
次のコード・スニペットの例は、OCIJson*
記述子との間で可変モードでDOMドキュメントの参照を設定する方法を示しています。
/* Set the JSON DOM MUTABLE Attribute to TRUE */
boolean attr = TRUE;
rc = OCIAttrSet(jsond, /* OCIJson descriptor */
OCI_DTYPE_JSON, /* Descriptor type */
&attr,
OCI_ATTR_JSON_DOM_MUTABLE, /* Attribute type */
errhp);
if (rc != OCI_SUCCESS) goto err_hndlr;
OCI_ATTR_JSON_DOM_MUTABLE
記述子属性にTRUE
を設定するには、OCIAttrSet()
を使用します。
- バッファ入力 –
oratext*
- ストリーミング入力 –
orastream *
23.2.2 不変DOMとしてのJSONのマニフェスト
OCIクライアント・アプリケーションは、バイナリJSONに基づく不変の線形バイナリ・バッファとしてJSONコンテンツをマニフェストできます。
次のコード・スニペットの例は、OCIJson*
記述子との間で不変モードでDOMドキュメントの参照を設定する方法を示しています。この設定を記述子に対して行わない場合は、値FALSE
にデフォルト設定されます。
/* Set the JSON DOM IMMUTABLE Attribute to FALSE */
boolean attr = FALSE;
rc = OCIAttrSet(jsond, /* OCIJson descriptor */
OCI_DTYPE_JSON, /* Descriptor Type */
&attr,
OCI_ATTR_JSON_DOM_MUTABLE, /* Attribute type */
errhp);
if (rc != OCI_SUCCESS) goto err_hndlr;
OCI_ATTR_JSON_DOM_MUTABLE
にFALSE
を設定するには、OCIAttrSet()
を使用します。
- バッファ入力 –
oratext*
- ストリーミング入力 –
orastream *
23.3 JSONデータの書込みおよび読取りのコール順序
この項では、データベースへのJSONデータの書込みおよび読取りのコール順序について説明します。
データベースにJSONデータを書き込むためのコール順序を次の図に示します。
23.4 JSON DOMの操作
この項では、様々なJSON DOMの操作について説明します。
23.4.1 スカラー型のマッピング
この項では、サポートされるスカラー型のマッピングをリストして説明します。
テキストJSONでは、標準JSON言語のスカラー型のみがサポートされます。ただし、JSONデータのSQL型がJSONの場合、Oracle Databaseでは標準のJSON言語型のセットが拡張されており、SQLスカラー・データ型(binary、date、timestamp、double、float、year-month interval、day-second intervalなど)に直接対応する複数のスカラー型が含まれています。
これと同様に、プログラム変数は拡張スカラー型に直接マッピングされるため、OCIJson
を使用してデータベースからJSON値がフェッチされます。これらのプリミティブ型のマッピングのサマリーを次の表に示します。
表23-2 スカラー型のマッピング
JSONスカラー型 | データベースのSQL型 | OCI SQL型定数 | OCIプログラム変数 | JSONスカラー型定数 |
---|---|---|---|---|
|
|
|
oratext[n] |
JZNVAL_STRING |
|
|
|
OCINumber* |
JZNVAL_ORA_NUMBER |
|
該当なし |
該当なし | 該当なし | JZNVAL_TRUE |
|
該当なし | 該当なし | 該当なし | JZNVAL_FALSE |
|
|
|
ub1[n] |
JZNVAL_BINARY |
|
|
|
double |
JZNVAL_DOUBLE |
|
BINARY_FLOAT |
SQLT_BFLOAT |
float |
JZNVAL_FLOAT |
|
|
|
JsonDateTime* |
JZNVAL_ORA_DATE |
|
|
|
JsonDateTime* |
JZNVAL_ORA_TIMESTAMP |
|
|
|
JsonDayInterval* |
JZNVAL_ORA_DAYSECOND_DUR |
|
|
|
JsonYearInterval* |
JZNVAL_ORA_YEARMONTH_DUR |
|
該当なし | 該当なし | 該当なし | JZNVAL_NULL |
関連項目:
23.4.2 JSON DOMスカラー・ノードの読取り
この項では、JSON DOMスカラー・ノードを読み取る方法について説明します。
OCIは、バイナリの単精度浮動小数点と倍精度浮動小数点をIEEEの単精度浮動小数点形式および倍精度浮動小数点形式に暗黙的に変換します。number、timestamp、day-second intervalおよびyear-month interval型の場合は、JsonOCIVal *
型を使用し、JsonDomGetScalarInfoOci()
関数を使用して構造化された形式でデータを読み取ることができます。JsonOCIVal *
は、次のコード・スニペットに示されているように定義されたCの共用体です。
/* Auxiliary Union of helper structures */
typedef union JsonOCIVal
{
JsonDateTime dt_JsonOCIVal;
JsonDayInterval dayInv_JsonOCIVal;
JsonYearInterval yrInv_JsonOCIVal;
ub1 num_JsonOCIVal[JZN_ORA_NUM_MAX_LEN];
} JsonOCIVal;
#include <ocijson.h>
void introspectDomNode(appctx *c,
JsonDomDoc *doc,
JsonDomNode *node)
{
JsonOCIVal av;
jznScalarVal sval;
jznnodetype ntype;
/* Check the JSON node type */
ntype = JsonDomGetNodeType(doc, node);
if (ntype == JZNDOM_SCALAR)
{
/* Get information for this JSON scalar node */
JsonDomGetScalarInfoOci(doc, (JsonDomScalar *)node, &sval, &av);
printScalarInfo(c, &sval, &av);
}
else if (ntype == JZNDOM_ARRAY) {...}
else if (ntype == JZNDOM_OBJECT) {...}
...
}
void printScalarInfo(appctx *c,
jznScalarVal *sval,
JsonOCIVal *av)
{
jznvaltype vtype = sval->type_jznScalarVal;
ub4 i;
switch (vtype)
{
case JZNVAL_STRING:
printf("Type: JZNVAL_STRING\n");
printf("Value: %.*s\n", sval->len_jznScalarVal, sval->val_jznScalarVal);
break;
case JZNVAL_BINARY:
printf("Type: JZNVAL_BINARY\n");
printf("Value: ");
for (i = 0 ; i < sval->binlen_jznScalarVal; i++)
printf("%X", (sval->binval_jznScalarVal)[i]);
printf("\n");
break;
case JZNVAL_FLOAT:
printf("Type: JZNVAL_FLOAT\n");
printf("Value: %f\n", sval->flt_jznScalarVal);
break;
case JZNVAL_DOUBLE:
printf("Type: JZNVAL_DOUBLE\n");
printf("Value: %lf\n", sval->db_jznScalarVal);
break;
case JZNVAL_TRUE:
printf("Type: JZNVAL_TRUE\n");
break;
case JZNVAL_FALSE:
printf("Type: JZNVAL_FALSE\n");
break;
case JZNVAL_NULL:
printf("Type: JZNVAL_NULL\n");
break;
case JZNVAL_ORA_NUMBER:
{
ub4 nval;
printf("Type: JZNVAL_ORA_NUMBER\n");
OCINumberToInt(c->errhp, (const OCINumber *) av, (uword) sizeof(ub4),
OCI_NUMBER_UNSIGNED, &nval);
printf("Value: %d\n", nval);
break;
}
case JZNVAL_ORA_DATE:
{
JsonDateTime *ts;
printf("Type: JZNVAL_ORA_DATE\n");
ts = &(av->dt_JsonOCIVal);
printf("Value: %d-%d-%d\n", ts->year_JsonDateTime, ts->month_JsonDateTime,
ts->day_JsonDateTime);
break;
}
case JZNVAL_ORA_TIMESTAMP:
{
JsonDateTime *ts;
printf("Type: JZNVAL_ORA_TIMESTAMP\n");
ts = &(av->dt_JsonOCIVal);
printf("Value: %d-%d-%d %d:%d:%d:%d\n", ts->year_JsonDateTime,
ts->month_JsonDateTime, ts->day_JsonDateTime, ts->hour_JsonDateTime,
ts->minute_JsonDateTime, ts->second_JsonDateTime,
ts->fsecond_JsonDateTime);
break;
}
case JZNVAL_ORA_YEARMONTH_DUR:
{
JsonYearInterval *yint;
printf("Type: JZNVAL_ORA_YEARMONTH_DUR\n");
yint = &(av->yrInv_JsonOCIVal);
printf("Value: %dY-%dM\n",
yint->years_JsonYearInterval, yint->months_JsonYearInterval);
break;
}
case JZNVAL_ORA_DAYSECOND_DUR:
{
JsonDayInterval *dint;
printf("Type: JZNVAL_ORA_DAYSECOND_DUR\n");
dint = &(av->dayInv_JsonOCIVal);
printf("Value: %dD-%dH-%dM-%dS-%dSS\n",
dint->days_JsonDayInterval, dint->hours_JsonDayInterval,
dint->minutes_JsonDayInterval, dint->seconds_JsonDayInterval,
dint->fseconds_JsonDayInterval);
break;
}
default:
printf("ERROR: Unsupported value type encountered [%d]\n", vtype);
break;
}
}
関連項目:
jznScalarValデータ型23.4.3 JSON DOMの作成
この項では、JSON DOMの作成方法について説明します。
23.4.3.1 JSONスカラー型とスカラー・コンストラクタ
この項では、JSONスカラー型および対応するスカラー・コンストラクタを示します。
可変DOMでは、新しいノードを追加したり、既存のノードを変更したりできます。次の表に、スカラー型およびその対応するコンストラクタ関数を示します。
表23-3 スカラー型とコンストラクタ
JSONスカラー型 | スカラー・コンストラクタ |
---|---|
|
|
|
|
|
|
|
|
|
JsonDomCreateBinary |
|
JsonDomCreateDouble |
|
|
|
|
|
|
|
|
非スカラー型のコンストラクタ関数
次の表に、非スカラー型(配列およびオブジェクト)のコンストラクタ関数を示します。JSONノード・タイプ | スカラー・コンストラクタ |
---|---|
JZNDOM_ARRAY |
JsonDomCreateArray |
JZNDOM_OBJECT |
JsonDomCreateObject |
注意:
JsonDomSetField()
関数を使用すると、指定したフィールドの値をDOM内の指定したオブジェクトに設定できます。
関連項目:
JSON DOM関数23.4.3.2 スカラー・ノードを使用したDOMの作成
この項では、様々な型のスカラー・ノードを使用してDOMを作成する方法について説明します。
- JSON記述子を割り当て、可変プロパティを設定します
- 様々な型(string、number、boolean、binary、double、float、date、timestamp、null)のスカラー・ノードを作成し、これらのノードをDOMに追加します
- 記述子にJSON DOMコンテナを設定します
- JSON記述子をテキストにシリアライズします
- 記述子を解放します
#include <ocijson.h>
sword buildDom(appctx *c,
JsonDomDoc *jdoc,
JsonDomObject *root,
boolean ismut)
{
OCIJson *jsond;
oratext outbuf[1024] = {0};
oraub8 outlen = 1024;
JsonDomScalar *node;
/* Field names */
oratext *s_name = (oratext *) "string_val";
oratext *n_name = (oratext *) "number_val";
oratext *bt_name = (oratext *) "true_val";
oratext *bf_name = (oratext *) "false_val";
oratext *b_name = (oratext *) "binary_val";
oratext *d_name = (oratext *) "double_val";
oratext *f_name = (oratext *) "float_val";
oratext *dt_name = (oratext *) "date_val";
oratext *dtt_name = (oratext *) "datetime_val";
oratext *nl_name = (oratext *) "null_val";
/* Values */
oratext *sval = (oratext *) "Strings are sequence of characters";
ub4 slen = (ub4) strlen(sval);
int inval = -29873546;
OCINumber nval;
boolean btval = TRUE;
boolean bfval = FALSE;
ub1 bval[8] = {0x000D, 0x000E, 0x000A, 0x000D,
0x000B, 0x000E, 0x000E, 0x000F};
ub4 blen = (ub4) 8;
double dval = 34837749.5699837;
float fval = -133424.75;
OCIDate *odval = NULL;
sb2 yrval = 2020;
ub1 mnval = 10;
ub1 dyval = 25;
OCIDateTime *odtval = NULL;
ub1 hrval = 8;
ub1 minval = 32;
ub1 secval = 56;
ub4 fsecval = 123456789;
/* (1) Allocate JSON descriptor and set mutable property */
checkerr("Allocate JSON descriptor", c,
OCIDescriptorAlloc(c->envhp, (void **) &jsond,
OCI_DTYPE_JSON, 0, 0));
checkerr("Attr set mutable", c,
OCIAttrSet((void *) jsond, OCI_DTYPE_JSON, &ismut, 0,
OCI_ATTR_JSON_DOM_MUTABLE, c->errhp));
/* (2) Create scalar fields and add to DOM */
/* (a) Add string field */
node = JsonDomCreateString(jdoc, sval, slen);
JsonDomSetField(jdoc, root, s_name, (ub2) strlen(s_name),
(JsonDomNode *) node);
/* (b) Add number field */
checkerr("Create OCINumber", c,
OCINumberFromInt(c->errhp, &inval, (uword) sizeof(int),
(uword) OCI_NUMBER_SIGNED, &nval));
node = JsonDomCreateOCINumber(jdoc, &nval);
JsonDomSetField(jdoc, root, n_name, (ub2) strlen(n_name),
(JsonDomNode *) node);
/* (c) Add boolean TRUE field */
node = JsonDomCreateBoolean(jdoc, btval);
JsonDomSetField(jdoc, root, bt_name, (ub2) strlen(bt_name),
(JsonDomNode *) node);
/* (d) Add boolean FALSE field */
node = JsonDomCreateBoolean(jdoc, bfval);
JsonDomSetField(jdoc, root, bf_name, (ub2) strlen(bf_name),
(JsonDomNode *) node);
/* (e) Add binary field */
node = JsonDomCreateBinary(jdoc, bval, blen);
JsonDomSetField(jdoc, root, b_name, (ub2) strlen(b_name),
(JsonDomNode *) node);
/* (f) Add double field */
node = JsonDomCreateDouble(jdoc, dval);
JsonDomSetField(jdoc, root, d_name, (ub2) strlen(d_name),
(JsonDomNode *) node);
/* (g) Add float field */
node = JsonDomCreateFloat(jdoc, fval);
JsonDomSetField(jdoc, root, f_name, (ub2) strlen(f_name),
(JsonDomNode *) node);
/* (h) Add date field */
checkerr("Create OCIDate", c,
OCIDescriptorAlloc(c->envhp, (void **) &odval, OCI_DTYPE_DATE,
0, NULL));
OCIDateSetDate(odval, yrval, mnval, dyval);
node = JsonDomCreateOCIDate(jdoc, odval);
JsonDomSetField(jdoc, root, dt_name, (ub2) strlen(dt_name),
(JsonDomNode *) node);
/* (i) Add datetime field */
checkerr("Create OCIDateTime", c,
OCIDescriptorAlloc(c->envhp, (void **) &odtval, OCI_DTYPE_TIMESTAMP,
0, NULL));
OCIDateTimeConstruct (c->envhp, c->errhp, odtval,
yrval-10, mnval-5, dyval-10,
hrval, minval, secval, fsecval, NULL, 0);
node = JsonDomCreateOCIDateTime(jdoc, odtval);
JsonDomSetField(jdoc, root, dtt_name, (ub2) strlen(dtt_name),
(JsonDomNode *) node);
/* (j) Add NULL field */
node = JsonDomCreateNull(jdoc);
JsonDomSetField(jdoc, root, nl_name, (ub2) strlen(nl_name),
(JsonDomNode *) node);
/* (3) Set the JSON DOM container in the descriptor */
checkerr("Set JSON DOM container", c,
OCIJsonDomDocSet(c->svchp, jsond, jdoc, c->errhp, 0));
/* (4) Serialize JSON descriptor to text */
checkerr("To Text Buffer", c,
OCIJsonToTextBuffer(c->svchp, jsond, outbuf, &outlen,
JZNU_PRINT_PRETTY, c->errhp,
OCI_JSON_TEXT_ENV_NLS));
printf("Descriptor content:\n");
printf("%.*s \n", outlen, outbuf);
finally:
/* (5) Free the descriptors */
if (odval)
checkerr("Free Date descriptor", c,
OCIDescriptorFree(odval, OCI_DTYPE_DATE));
if (odtval)
checkerr("Free DateTime descriptor", c,
OCIDescriptorFree(odtval, OCI_DTYPE_TIMESTAMP));
if (jsond)
checkerr("Free JSON descriptor", c,
OCIDescriptorFree(jsond, OCI_DTYPE_JSON));
return c->status;
}
OCIJsonToTextBuffer()
関数は、次のテキストJSONの出力を戻します。{
"string_val" : "Strings are sequence of characters",
"number_val" : -29873546,
"true_val" : true,
"false_val" : false,
"binary_val" : "0D0E0A0D0B0E0E0F",
"double_val" : 34837749.5699837,
"float_val" : -133424.75,
"date_val" : "2020-10-25T00:00:00",
"datetime_val" : "2010-05-15T08:32:56.123456789",
"null_val" : null
}
関連項目:
JSON DOM関数23.5 JSON記述子を使用したマルチスレッド
OCIJson
*記述子はスレッド・セーフではありません。記述子とその子孫のDOMノードが一度に1つのスレッドのみで操作されるようにすることは、ユーザーの責任です。
23.6 キャラクタ・セットの処理
OCIでは、テキスト入力のキャラクタ・セットは、OCI環境ハンドルの設定によって異なります。
OCIEnv*
の作成時にユーザーがcsid
パラメータを指定しない場合、NLS_LANG
設定がハンドルのデフォルト設定として使用されます。テキストJSONが入力であるOCIJsonTextBufferParse()
、OCIJsonTextStreamParse()
などのAPIの場合、入力は、Oracleで認識される任意のキャラクタ・セットにすることができ(JSON構文に準拠している場合)、環境ハンドルまたはNLS_LANG
パラメータに設定されているキャラクタ・セットである必要はありません。
注意:
Unicodeでエンコードされた入力にJZN_INPUT_DETECT
を使用する場合、入力のエンコーディングはUTF-8、UTF-16 (LEまたはBE)のいずれかとして検出され、それに応じて処理されます。テキストJSONの入力がこのモードのUnicodeのエンコーディングのいずれかではない場合は、ユーザー・エラーであり、動作は保証されません。
OCI_JSON_TEXT_ENV_NLS
モードが設定されている場合を除き、OCIJson API (OCIJsonToTextBuffer ()
、OCIJsonToTextStream ()
など)は、テキストJSONをAL32UTF8キャラクタ・セットで戻します。