ActionScript 3 (Flash Player や AIR で動くプログラムの言語) では、長いあいだ、JSON を扱うための標準の仕組みがありませんでした。そのため、as3corelib というライブラリーの JSON クラスが広く使われていました。ところが、2011 年に公開された Flash Player 11 と AIR 3 で、標準の JSON クラスが加わります。すると、ライブラリーの JSON クラスを使っていたコードが、そのままではコンパイルできなくなることがありました。ここでは、この名前の衝突が起きた理由と解決のしかたを説明します。

何が起きたのか

as3corelib の JSON クラスは、com.adobe.serialization.json パッケージにあり、次のように使っていました。

import com.adobe.serialization.json.JSON;

// 文字列からオブジェクトへ
var data:Object = JSON.decode('{"name":"sample","count":3}');
// オブジェクトから文字列へ
var text:String = JSON.encode(data);

Flash Player 11 向けにコンパイルすると、標準の JSON クラスも見えるようになります。標準の JSON はパッケージに属さない、トップレベルのクラスです。Array や Date と同じく、import しなくても、どこからでも使えます。

その結果、コードの中の JSON という名前が、ライブラリーのクラスと標準のクラスのどちらを指すのか決められなくなり、「参照があいまいである」という意味のコンパイルエラーになりました。古い版の Flash Player 向けにコンパイルしている限りは、標準の JSON が無いので、エラーは起きません。対象の版を上げたとたんに起きるのが、この問題の分かりにくいところでした。

なぜ名前がぶつかるのか

ActionScript 3 では、クラスの本当の名前は、パッケージを含めた完全な名前 (完全修飾名) です。ライブラリーのクラスは com.adobe.serialization.json.JSON、標準のクラスは単に JSON です。

import は、パッケージを書かずに短い名前でクラスを使えるようにする宣言です。ライブラリーのクラスを import すると、短い名前の JSON が 2 つ見えることになります。どちらを使うべきかは、コンパイラーには判断できません。ActionScript 3 には、import するときに別の名前を付ける仕組みも無いので、短い名前のままでは避けようがありません。

解決のしかた

完全な名前で書く

すぐに直したいときは、ライブラリーのクラスを使う箇所を、パッケージを含めた完全な名前で書きます。完全な名前なら、どちらのクラスかがはっきりするので、エラーは消えます。

import com.adobe.serialization.json.JSON;

// パッケージを含めた完全な名前で書けば、標準の JSON と区別できる
var data:Object = com.adobe.serialization.json.JSON.decode(source);
var text:String = com.adobe.serialization.json.JSON.encode(data);

標準の JSON に移す

対象を Flash Player 11 以降にするなら、ライブラリーをやめて、標準の JSON クラスに移すのが本筋です。プレーヤーに組み込まれた処理なので、ライブラリーより速く動くことが期待できます。

// トップレベルのクラスなので、import は要らない
var data:Object = JSON.parse(source);
var text:String = JSON.stringify(data);
// 3 番目の引数で字下げを指定すると、読みやすい形で書き出せる
var pretty:String = JSON.stringify(data, null, 2);

try {
  var result:Object = JSON.parse(input);
} catch (error:SyntaxError) {
  // JSON として正しくない文字列を渡すと、例外が起きる
  trace("JSON として読めません: " + error.message);
}

メソッドの名前は decode から parse へ、encode から stringify へ変わります。読めない文字列を渡したときの例外の種類も、ライブラリーの独自の例外 (JSONParseError) から SyntaxError に変わります。クラスのインスタンスをどう文字列にするかなど、細かな動きは同じとは限らないので、置き換えたら出力を確かめます。

JavaScript の JSON との違い

標準の JSON の使い方は、JavaScript の JSON とよく似ています。どちらも JSON.parse() と JSON.stringify() を持ち、引数の考え方も共通です。ActionScript 3 では静的なメソッドを持つクラス、JavaScript では関数をまとめたオブジェクトという違いはありますが、書き方はほとんど同じです。

名前の衝突の扱いは違います。JavaScript のモジュールで、自分で JSON という名前を import しても、エラーにはなりません。そのモジュールの中では、import したほうが標準の JSON より優先されます。エラーにならない分、気づかずに別のものを使っていることがあるので、標準と同じ名前は付けないのが安全です。

// lib.js が独自の JSON を書き出していると、このモジュールの中では標準の JSON が隠れる
import { JSON } from "./lib.js";

JavaScript でも、昔のブラウザーには標準の JSON がありませんでした。そのころ広く使われた json2.js というライブラリーは、標準の JSON が無いときだけ自分の実装を用意する作りで、衝突を避けていました。

今の Flash と ActionScript

Flash Player は 2020 年末でサポートが終わり、今の主なブラウザーでは動きません。Web のページで ActionScript のコードを書くことは、もうありません。Flash で作ったページを今も使いたい場合は、JavaScript と HTML で作り直すことになります。そのとき、JSON を扱う部分は JSON.parse() と JSON.stringify() にほぼそのまま置き換えられます。Flash で音を鳴らしていたページの移し方は、「スマートフォンの Web で音を鳴らす方法のまとめ」でも触れています。

まとめ

  • Flash Player 11 と AIR 3 で、ActionScript 3 にトップレベルの JSON クラスが加わった
  • as3corelib の com.adobe.serialization.json.JSON を import していると、短い名前の JSON が 2 つ見えて、コンパイルエラーになった
  • すぐ直すなら完全な名前で書く。Flash Player 11 以降が対象なら、標準の JSON.parse() と JSON.stringify() に移す
  • JavaScript の JSON も使い方は同じ。モジュールで同じ名前を import するとエラーにならずに標準のものが隠れるので、同じ名前は避ける
  • Flash Player は 2020 年末でサポートが終わった

参考