%Set() または %Push() を使用したデフォルトデータ型のオーバーライド
既定では、システムは自動的に、%Set() または %Push() の値引数をオブジェクトのデータ型 (object、array、または oref) または ObjectScript リテラルのデータ型 (string または number) として解釈します。JSON リテラルの null、true、または false を値として直接渡すことはできません。この引数は ObjectScript のリテラルまたは式として解釈されるからです。例えば、次のコードでは、値 true が変数名として解釈されるため、エラーがスローされます。
do o.%Set("prop3",true)
DO o.%Set("prop3",true)
^
<UNDEFINED> *true
ObjectScript は、null の代わりに "" (空の文字列) を使用し、ブーリアン値 false の代わりに 0 を使用し、ブーリアン値 true の代わりにゼロ以外の数字を使用します。この問題に対処するために、%Set() と %Push() は、対象の値のデータ型を指定するためのオプションの 3 つ目の引数を取ります。この 3 つ目の引数には、JSON の boolean または null を使用できます。以下に例を示します。
write {}.%Set("a",(2-4)).%Set("b",0).%Set("c","").%ToJSON()
{"a":-2,"b":0,"c":""}
write {}.%Set("a",(2-4),"boolean").%Set("b",0,"boolean").%Set("c","","null").%ToJSON()
{"a":true,"b":false,"c":null}
対象の値を数値として解釈可能な場合は、3 つ目の引数には string または number を使用することもできます。
write [].%Push("023"_"04").%Push(5*5).%ToJSON()
["02304",25]
write [].%Push(("023"_"04"),"number").%Push((5*5),"string").%ToJSON()
[2304,"25"]
JSON の NULL 値とブーリアン値の解決
JSON 構文では、true、false、および null という値は、1、0、および "" (空の文字列) という値とは異なりますが、ObjectScript ではこのような区別はされません。JSON の値が要素またはプロパティから取得されると、それらの値は常に ObjectScript に対応した値にキャストされます。すなわち、JSON の true は常に 1 として返され、false は 0 として返され、null は "" として返します。ほとんどの場合は、これは望ましい結果となります。その返り値は、最初に JSON フォーマットから変換することなく、ObjectScript の式で使用できるからです。ダイナミック・エンティティの内部には JSON や ObjectScript の元の値が保持されるため、%GetTypeOf() を使用して必要に応じて実際のデータ型を特定できます。
次の例では、動的配列コンストラクタによって、JSON の true、false、および null の各値、数値と文字列のリテラル値、および ObjectScript のダイナミック式 (評価結果が ObjectScript のブーリアン値 1 および 0 になるもの) が指定されます。
set test = [true,1,(1=1),false,0,(1=2),"",null]
write test.%ToJSON()
[true,1,1,false,0,0,"",null]
上記からわかるように、コンストラクタで割り当てられた値は、結果として得られる動的配列内に保持されていたため、JSON 文字列としてシリアル化されたときに適切に表示されます。
次の例では、配列の値を取得して表示します。予想どおり、JSON の値 true、false、および null は ObjectScript に対応した値 1、0、および "" にキャストされます。
set iter = test.%GetIterator()
while iter.%GetNext(.key,.val){write "/"_val_"/ "}
/1/ /1/ /1/ /0/ /0/ /0/ // //
この例では %GetNext() を使用していますが、%Get()、%Pop()、またはドット構文を使用して値を取得した場合でも同じ結果が得られます。
必要に応じて、%GetTypeOf() メソッドを使用して値の元のデータ型を確認できます。以下に例を示します。
set iter = test.%GetIterator()
while iter.%GetNext(.key,.val) {write !,key_": /"_test.%Get(key)_"/ = "_test.%GetTypeOf(key)}
0: /1/ = boolean
1: /1/ = number
2: /1/ = number
3: /0/ = boolean
4: /0/ = number
5: /0/ = number
6: // = string
7: // = null
Note:
ダイナミック・オブジェクト内のデータ型
この章では動的配列を主に扱っていますが、同じデータ型変換がダイナミック・オブジェクトの値にも適用されます。このセクションの例は、動的配列 test がダイナミック・オブジェクトに置き換わる場合でもまったく同じように動作します。
set test = {"0":true,"1":1,"2":(1=1),"3":false,"4":0,"5":(1=2),"6":"","7":null}
この行を除いて、サンプル・コードのどの部分も変更する必要はありません。このオブジェクト内のプロパティ名は、元の配列のインデックス番号に対応する数値文字列であるため、出力もまったく同じになります。
NULL、空の文字列、および未割り当て値の解決
JSON の null 値を要素やプロパティに割り当てることができますが、その値は常に "" (ObjectScript の空の文字列) として返されます。未割り当て要素の値を取得しようとした場合にも、空の文字列が返されます。%GetTypeOf() を使用して、各ケースの実際のデータ型を特定できます。
この例では、JSON の null 値と空の文字列が含まれたスパース配列を調べます。配列要素 2 には値が割り当てられていませんが、この要素は JSON 文字列内では null で表現されます。
set array = [null,""]
do array.%Set(3,"last")
write array.%ToJSON()
[null,"",null,"last"]
ほとんどの場合は、%GetNext() を使用して配列値を取得しますが、この例では、for ループを使用して、%GetNext() を使用した場合はスキップされる未割り当て値を返します。最終要素のインデックス番号は array.%Size()-1 ですが、ループ・カウンタは配列の末尾を越えて処理を続行するように意図的に設定されています。
for i=0:1:(array.%Size()) {write !,i_". value="""_array.%Get(i)_""" type="_array.%GetTypeOf(i)}
0. value="" type=null
1. value="" type=string
2. value="" type=unassigned
3. value="last" type=string
4. value="" type=unassigned
この例では、%Get() は以下の 4 つのケースで空の文字列を返します。
-
要素 0 は JSON の null 値であり、%GetTypeOf() によってデータ型 null として識別されます。
-
要素 1 は空の文字列であり、データ型 string として識別されます。
-
要素 2 は値を持っていないため、データ型 unassigned として識別されます。
-
要素 3 が最後の配列要素ですが、この例では、存在しない要素 4 のデータ型を取得しようとします。この要素もデータ型 unassigned として識別されます。有効な配列インデックス番号は常に array.%Size() より小さい値になります。
Note:
null と unassigned の区別は、ダイナミック・エンティティが JSON 文字列にシリアル化されたときに保持されない ObjectScript メタデータです。すべての unassigned 要素は null 値としてシリアル化されます。詳細は、“スパース配列と未割り当て値の理解” を参照してください。