業務アプリ開発講座

JSONデータの読み込み方法

主要ページ共通のページヘッダーをプログラミングします。

前々回作成した、メニューデータ(app-data/sitemap.json)を使用し、メニュータグを生成します。

今一度、メニューデータがどういう構造になっているか確認してみましょう。

まず、データ全体が角カッコ[ ]で囲まれています。つまり、配列になっているわけです。その第1階層の要素は波カッコ{ }で囲まれていますのでオブジェクトになります。つまり、配列の各要素がオブジェクトということになっています。。第1階層の各オブジェクトにはキーとしてnameが必ずあり、その値が「Home」や「売上管理」だったりしています。また、urlchildキーがあるオブジェクト、ないオブジェクトがあります。例えば「Home」にはchildキーはありませんし、「売上管理」にはurlキーがありません。このキーの有無に応じて生成するHTMLタグを振り分けるわけです。また、ページヘッダーとページボディ上に生成するメニューでは、メニューデータのうち、第2階層までを使用します。

include/classes/AppMasterHtml.phpに下記関数を追記します。

function navHeader(){
	$sitemap= file_get_contents('../app-data/sitemap.json');
	$encoded= mb_convert_encoding($sitemap, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
	$navData= json_decode($encoded, true);
}

このソースは完成形ではありません。まず、メニューデータであるsitemap.jsonを読み込みます。

$sitemap= file_get_contents(‘../app-data/sitemap.json’);

file_get_contents()関数は引数で指定したURLが返すコンテンツを受け取ります。ちなみに引数に[http://www.yahoo.co.jp/]と指定しますと、YahooのトップページのHTMLを受け取ります。

$encoded= mb_convert_encoding($sitemap, ‘UTF8’, ‘ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN’);

file_get_contents()で受け取ったコンテンツの文字コードをUTF-8に変換しています。sitemap.jsonはあらかじめUTF-8で作成していますので、ここでは必ずしも必要なわけではありません。ですが、念のため明示的に変換しています。例えば、リリース後に緊急でデータを変更する必要があった場合に、普段の開発で使用していないエディタ等で編集すると、場合によっては文字コードが変更されてしまうことがあります。そうするとデータが文字化けしてしまうことがあります。そういうことを避けるため、念を入れて文字コードを変換しておきます。

$navData= json_decode($encoded, true);

json_decode()関数はJSON形式の文字列をPHPで各要素にアクセスできるように変換します。ちなみにdecodeとは「解読する」という意味があります。反対に暗号化することをencodeと言います。
第2引数は、変換方式を指しています。
true : 連想配列に変換されます。例えば、Homeのnameにアクセスする場合、$navData[0]['name']と記述します。
false : オブジェクトに変化されます。$navData[0]->nameと記述します。

連想配列とオブジェクト、どちらを用いても大差はありません。強いてい言うならば、キーに変数を用いたい場合、オブジェクトにすると誤解を招く可能性があるくらいでしょうか。例えば、

$key = 'name';
$val = $navData[0][$key];	//連想配列
$val = $navData[0]->$key;	//オブジェクト

どちらも正しく値を取得できます。ただ、オブジェクトでキーに変数を用いると、パッと見誤解を招く可能性が無きにしもあらずという気がします。ですので、データを利用するプログラムで、キーを指定する際に変数を用いるかキー名をダイレクトに指定できかで使い分けると良いかもしれません。

さて、ここまででsitemapデータを使用する準備が整いました。続いては、データからメニュー名を取り出してみましょう。

データの第1階層は配列になっていましたね。まず、第1階層のnameを取得してみます。配列データを順番に取得するにはforeachを使いループ処理を行います。( )内に、まず配列データ変数を入れて、asを挟んで右の変数に配列データのうち一つの要素が順番に格納されていきます。そしてforeachのブロックの中で、一つ一つの要素に対して処理をしていきいます。ここではオブジェクト形式で記述していきます。

function navHeader(){
	$sitemap = file_get_contents('../app-data/sitemap.json');
	$encoded = mb_convert_encoding($sitemap, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
	$navData = json_decode($encoded, true);

	foreach($navData as $obj){
		echo $obj->name;
	}
}

まずは、第1階層のnameキーの値を画面に出力してみます。次にurlを画面に出力してみましょう。そうすると一部で、

Notice: Undefined property: stdClass::$url

このように表示されますね。これはキーurlが存在しないというエラーメッセージです。では、このエラーが出ないようにurlが存在する場合のみ、画面に出力するように条件文を追記してみましょう。

foreach($navData as $obj){
	if(isset($obj->url)){
		echo $obj->url;
	}
}

isset()という関数は、引数の変数が、セットされていて且つでnullでないことを検査します。
sitemapデータには、要素によってキー[url, child]があったりなかったりしています。そこでこのisset()を使って、要素に応じて処理を振り分けていきます。幸いsitemapデータにはキー[name]が無い要素はありませんので、第一階層のデータはこのように取得できます。今度はキーurlを直接画面に出力するのではなく変数に格納してから画面に出力します。

foreach($navData as $obj){
	$url	= isset($obj->url)? $obj->url : '';

	echo "{$obj->name} : {$url}
"; }

2行目に何やら新しい書き方が見えます。これはif{}else{}の略式方法です。PHPに限らず様々な言語で同じように記述できます。

変数 = 条件文? 条件文がtrueの場合の値 : 条件文がfalseの場合の値

という形になっていて、if文に直すと

if(条件文){
	変数 = 条件文がtrueの場合の値
}
else{
	変数 = 条件文がfalseの場合の値
}

と同じ意味になります。単純な振り分け処理ならば、簡潔に記述できるメリットがあります。ただし、複雑な条件を記述するとかえって読みにくくなりますので、加減を意識する必要があります。

今度はchild要素を読み込みます。

foreach($navData as $obj){
	$url	= isset($obj->url)? $obj->url : '';
	echo "{$obj->name} : {$url}
"; if(isset($obj->child)){ foreach($obj->child as $subObj){ $subUrl = isset($subObj->url)? $subObj->url : ''; echo "{$subObj->name} : {$subUrl}
"; } } }

5行目以下がchild要素を読み込んでいるところです。これまでに行った処理をchild要素に対して同じ処理を行っているだけです。これで、メニューを生成するデータを読み込むことができました。次回は読み込んだデータをHTMLタグに当てはめていきます。

今回のメニュー生成処理では2階層しかありませんので、このコーディングで問題ありません。しかし、もっと階層が深くなる場合、もっと発展すると階層の深さがわからない場合は、ループの中にループを書くやり方では表現し切れません。そういう場合は、再帰という方法を用いて処理します。再帰についてはパンくずリスト生成の時に解説します。