JSON データの操作
JavaScript Object Notation (JSON) は表現用の標準的なテキストベースの構造データ表現フォーマットで、JavaScript 構造データオブジェクトの表記法をベースとしています。一般的にはウェブアプリケーションでデータを転送する場合に使われます。(例えば、データをサーバーからクライアントへ送信する場合などで、ウェブページに表示されたりすることもあり、その逆もあります)。頻繁に見かけるデータフォーマットですので、この節では JavaScript を使って JSON をパースする、JSON のデータを参照する、JSON を作るなど、JSON を扱うために必要となる操作を説明します。
前提条件: | 基礎的なコンピュータの知識、HTML と CSS への基本的な理解、基礎的な JavaScript の理解 (JavaScript の第一歩と JavaScript の構成要素を参照) とオブジェクト指向JavaScript の基本 (JavaScript オブジェクトの基本を参照)。 |
---|---|
目的: | JSON内のデータの扱い方、JSON 文字列の作成方法について理解できること。 |
JSON とは何か
JSON は JavaScript オブジェクトの構文に従ったテキストベースのフォーマットで、Douglas Crockford によって普及されました。JSON は JavaScript オブジェクトの構文に似ていますが、JavaScript とは独立して扱われることがあり、多くのプログラミング言語環境には JSON を読み込む(パースする)したり生成したりする機能があります。
JSON は文字列です。ですので、ネットワークを通してデータを転送したい場合に便利です。JSON データへアクセスしたい場合は、JavaScript オブジェクトへ変換する必要があります。JavaScript には JSON と JavaScript オブジェクトを相互に変換できるメソッドを持った JSON というグローバルなオブジェクトがあるので、その変換は大きな問題ではありません。
注記: 文字列をネイティブオブジェクトへ変換することはデシリアライゼーション (deserialization) と呼ばれており、ネイティブオブジェクトをネットワークを通して転送できように文字列へ変換することはシリアライゼーション (serialization) と呼ばれています。
JSON 文字列はそれ自身をファイルとして格納することもできます。それは MIME type が application/json
で、.json
という拡張子の付いたただのテキストファイルです。
JSON の構造
上で説明したように、JSON は JavaScript オブジェクトにとても似ているフォーマットを持った文字列です。JSON では通常の JavaScript オブジェクトと同様な基本データ型(文字列、数値、配列、ブーリアンやその他のリテラル型)を使うことができます。これにより、以下のように階層的にデータを構成することができます。
'{
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true,
"members": [
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": [
"Radiation resistance",
"Turning tiny",
"Radiation blast"
]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
},
{
"name": "Eternal Flame",
"age": 1000000,
"secretIdentity": "Unknown",
"powers": [
"Immortality",
"Heat Immunity",
"Inferno",
"Teleportation",
"Interdimensional travel"
]
}
]
}'
もし、この文字列を JavaScript プログラムへ読み込んだ場合(例えば、例えば変数superHeroes
へ代入する)、JavaScript オブジェクトの基本の節で見たのと同様に ドットや角括弧を使ってデータへアクセスすることができます。例としては以下のようになります。
superHeroes.homeTown
superHeroes['active']
さらに深い階層のデータへアクセスする場合は、単純にプロパティ名や配列のインデックスを連結します。例えば、メンバーリスト中2番目のヒーローの 3番目の能力を参照する場合は、以下のようになります。
superHeroes['members'][1]['powers'][2]
- まず、変数名
superHeroes
を指定します。 - その中の
members
プロパティへアクセスしたいので、["members"]
と指定します。 members
にはオブジェクトの配列が格納されています. ここでは、配列内の 2番目のオブジェクトへアクセスするので、[1]
を指定します。- そのオブジェクト内で、
powers
プロパティへアクセスするため,["powers"]
と指定します。 powers
プロパティは選択したヒーローの能力を含んだ配列となっており、その中の 3番目が欲しいので、[2]
と記述します。
注記: 上記の JSON は JSONTest.html で参照することができます。(ページ内の source code を参照してください)。ページを読み込んで見て、ブラウザーのコンソールで変数内のデータにアクセスしてみてください。
JSON 配列
上記で、JSON テキストは基本的に文字列に入った JavaScript オブジェクトのように見えることを説明しました。配列を JSON との間で変換することもできます。以下も有効な JSON です。例:
'[
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": [
"Radiation resistance",
"Turning tiny",
"Radiation blast"
]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
}
]'
これも有効な JSON であり、パースしたデータには配列のインデックスを指定するだけです。例えば、[0]["powers"][0]
のように表記できます。
その他の注意点
- JSON は指定されたデータフォーマットの純粋な文字列であり、プロパティのみを含むことができ、メソッドを含むことができません。
- JSON では文字列とプロパティ名をダブルクォートで括る必要があります。シングルクォートは、JSON 文字列全体を囲む以外では無効です。
- 1 つだけカンマやコロンが抜けているだけで無効な JSON になりえます。なので、使用しているデータが有効であるかについては注意してみなければなりません(機械的に作った JSON のほうが、プログラムに問題がなければ、エラーは少なく済みます)。 JSONLint のようなアプリケーションを使って妥当性検査をすることもできます。
- JSON は配列やオブジェクトに限らず JSON内に含むことができるデータ型のデータだけでも有効な JSON となります。 例えば、1 つだけの文字列や数値も有効な JSON です。
- プロパティがクォートで括られていない JavaScript コードと異なり、JSON では、クォートされた文字列だけがプロパティとして使われます。
手を動かして学ぼう: JSON をさわってみる
それでは、Web サイト上でどのように JSON 形式のデータを使うことができるか例を通して見てみましょう。
はじめに
まず、heroes.html と style.css のコピーをローカルに作成してください。後者は例題ページをスタイリングするための CSS であり、前者は簡単な形式の HTML です。
<header>
</header>
<section>
</section>
他には、この演習で書く JavaScript を含んだ <script>
要素があります。この時点では、<header>
要素と <section>
要素 を取得して、変数へ代入している 2行コードのみが書かれています。
const header = document.querySelector('header');
const section = document.querySelector('section');
演習用の JSON データは https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json に用意してあります。
そのデータを演習ページに読み込んで、それを表示するのにいくらかの DOM操作を行います。最終的には、以下の画像のようになります。
JSON の取得
JSON を取得するには、XMLHttpRequest
(しばしば XHR と呼ばれる) という API を使用します。これは非常に便利な JavaScript オブジェクトで、JavaScript を使用してサーバからリソース (例:画像、テキスト、JSON、さらには HTML スニペットなど) を取得するネットワークリクエストを行うことができます。つまりページ全体を再読み込みせずに、小さな部分のコンテンツを更新することができます。これにより、よりレスポンシブな Web ページを作成できますが、それをもっと詳細に教えるのはこの記事の範囲を超えています。
- まず、取得したい JSON がある URL を変数へ代入します。次のコードを JavaScript の最後の行へ追加してください。
let requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
- HTTP リクエストを作成するのに、
new
を使ってXMLHttpRequest
から新しいリクエストオブジェクトをつくる必要があります。先ほどのコードの下に、次のコードを追加してください。let request = new XMLHttpRequest();
- 新しいリクエストを開始するのには
open()
メソッドを使います。 次のコードを追加してください。request.open('GET', requestURL);
このメソッドは最低2つのパラメータを引数として取ります(他に任意の引数を与えることもできます)。今回の簡単な例では、次の2つの必須パラメータのみを利用します。
- リクエストを開始する際に HTTP のメソッドを決める必要があります。今回のケースでは、単純にデータを取得するだけですので
GET
が良いでしょう。 - リクエストを送る先の URL。今回は JSON ファイルが置かれている URL です。
- リクエストを開始する際に HTTP のメソッドを決める必要があります。今回のケースでは、単純にデータを取得するだけですので
- 次に、以下の2行のコードを追加してください。XHR オブジェクトがサーバーから返されるデータを判断できるように
responseType
に JSON を指定します。すると、ブラウザ側で JavaScript オブジェクトへ変換してくれます。それから、send()
メソッドでリクエストを送信します。request.responseType = 'json'; request.send();
- 最後に、サーバーからのレスポンスを待ち、それを処理するコードを用意するので、以下のコードをこれまでのコードの下に追加してください。
request.onload = function() { const superHeroes = request.response; populateHeader(superHeroes); showHeroes(superHeroes); }
ここでは、先ほどのリクエストに対するレスポンス (response
プロパティから取得できます) を superHeroes
という変数へ代入しています。つまり、この変数に JSON を元に生成された JavaScript オブジェクトが格納されているということです! それから 2 つの関数をそのオブジェクトを引数として与えて呼び出しています。最初の関数は引数のデータを <header>
へ埋め込み、2 つ目は各ヒーローごとのインフォメーションカードを作り、<section>
へ埋め込みます。
上記の処理は、リクエストオブジェクトで load イベントが発生した時に呼び出される関数 (onload
を参照) に記述しました。このイベントはレスポンスがうまく取得できた場合に呼び出されるので、 request.response
を使って何か処理をしようとしたときに、それが必ず利用できることが保証されています。
ヘッダーへの値のセット
ここまでで、JSON の取得と JavaScript オブジェクトへの変換ができました、先ほどの 2 つの関数を実装して使ってみましょう。まずはじめに、以下のコードをこれまでのコードの下に追加してください。
function populateHeader(obj) {
const myH1 = document.createElement('h1');
myH1.textContent = obj['squadName'];
header.appendChild(myH1);
const myPara = document.createElement('p');
myPara.textContent = 'Hometown: ' + obj['homeTown'] + ' // Formed: ' + obj['formed'];
header.appendChild(myPara);
}
まず、createElement()
で <h1>
要素を生成、その textContent
プロパティにそのオブジェクトの squadName
プロパティをセット、そしてそれを appendChild()
を使いヘッダーに追加します。そして要素の生成、テキストのセット、ヘッダーへの追加という同じような操作をパラグラフ要素でも行います。セットするテキストの値が homeTown
と formed
プロパティの文字列を結合したものであるという点だけが異なります。
ヒーローインフォメーションカードの作成
次に、以下の関数をコードの下へ追記してください。この関数はスーパーヒーローカードの作成と画面表示を行います。
function showHeroes(jsonObj) {
const heroes = jsonObj['members'];
for (let i = 0; i < heroes.length; i++) {
const myArticle = document.createElement('article');
const myH2 = document.createElement('h2');
const myPara1 = document.createElement('p');
const myPara2 = document.createElement('p');
const myPara3 = document.createElement('p');
const myList = document.createElement('ul');
myH2.textContent = heroes[i].name;
myPara1.textContent = 'Secret identity: ' + heroes[i].secretIdentity;
myPara2.textContent = 'Age: ' + heroes[i].age;
myPara3.textContent = 'Superpowers:';
const superPowers = heroes[i].powers;
for (let j = 0; j < superPowers.length; j++) {
const listItem = document.createElement('li');
listItem.textContent = superPowers[j];
myList.appendChild(listItem);
}
myArticle.appendChild(myH2);
myArticle.appendChild(myPara1);
myArticle.appendChild(myPara2);
myArticle.appendChild(myPara3);
myArticle.appendChild(myList);
section.appendChild(myArticle);
}
}
始めに、JavaScript オブジェクトの members
プロパティを新しい変数に保存します。
この配列は複数のオブジェクトを持ち、オブジェクトはそれぞれのヒーローについての情報を持ちます。
次に、for ループを使って配列の個々のオブジェクトについてループしていきます。それぞれについて:
- 新しい要素をいくつか作ります:
<article>
1つ、<h2>
1つ、3つの<p>
と1つの<ul>
です。 <h2>
の中身を今のヒーローの名前 (name
) にします。- 3つの
<p>
の中身を、それぞれのsecretIdentity
とage
、リストにある情報を紹介していくために「超能力 ("Superpowers:")」で始まる行とします。 powers
プロパティをsuperPowers
という新しい定数に保存します — この定数は今のヒーローの超能力のリストを持つ配列です。- 別の
for
ループをつかって、今のヒーローの超能力をループします — それぞれに対する<li>
要素を作成し、超能力をこの要素の中身とし、<ul>
要素(myList
変数)のlistItem
にappendChild()
を使って追加します。 - 最後の最後にやるのは、
<h2>
、<p>
、<ul>
を<article>
(myArticle
) の中身に追加し、それから<article>
を<section>
の中身に追加します。HTML の中身として表示される順序になりますので、これらの要素が追加された順序は重要です。
付記: 例を動かしてみるのに問題があったら、heroes-finished.html ソースコードを参照して見て下さい(こちらで ライブ実行 もできます)。
付記: もし JavaScript オブジェクトにアクセスするのに使っているドット/ブラケット記法をなぞっていくのが難しければ、superheroes.json のファイルを別のタブやテキストエディタで開いておいて、JavaScript と並べて読んでいくとわかりやすいかもしれません。JavaScript オブジェクトの基本 記事にも戻って、ドット/ブラケット記法について読み返してみてください。
オブジェクトとテキスト間の変換
上の例は XHR リクエストで JSON レスポンスを直接JavaScript オブジェクトに変換していたので、JavaScript オブジェクトへのアクセスという面では単純でした。次の部分です:
request.responseType = 'json';
時にはこんなにツイていない場合もあります — 時には生の JSON 文字列を受けとり、自分でオブジェクトに変換しなければならない場合もあるでしょう。また JavaScript オブジェクトをネットワーク越しに送信したい場合、送信する前に JSON 文字列に変換しなければならないでしょう。ツイている事に、ウェブ開発でこの二つの問題にはしょっちゅう出くわすので、ブラウザには組込みの JSON オブジェクトが備わっていて、これは以下二つのメソッドを備えています:
parse()
: JSON文字列を引数に取り、それに対する JavaScript オブジェクトを返します。stringify()
: オブジェクトを引数に取り、等価な JSON 文字列を返します。
一つめの方の動作例が heroes-finished-json-parse.html にあります (ソース を見て下さい) — ここでは前の方で作成した例と全く同じ事をしていますが、XHR では生の JSON 文字列を返させて、それを parse()
で JavaScript オブジェクトに変換しているところだけが異なります。コードの重要な箇所はこの部分です:
request.open('GET', requestURL);
request.responseType = 'text'; // now we're getting a string!
request.send();
request.onload = function() {
const superHeroesText = request.response; // get the string from the response
const superHeroes = JSON.parse(superHeroesText); // convert it to an object
populateHeader(superHeroes);
showHeroes(superHeroes);
}
で、ご想像の通り stringify()
は全く反対の向きに動作します。次の行をブラウザーの JavaScript コンソールに一つずつ打ち込んでいって、実際に動かしてみて下さい:
let myObj = { name: "Chris", age: 38 };
myObj
let myString = JSON.stringify(myObj);
myString
JavaScript オブジェクトを作成してその中身を確認し、次に stringify()
を使って JSON 文字列に変換し — 戻り値を新しい変数に保存しています — その値も確認しています。
あなたのスキルをテストしてみましょう!
この記事はここまでですが、最も重要な情報を覚えていますか?先に進む前に、この情報を保持しているかどうかを確認するためのテストがいくつかあります — Test your skills: JSON (en-US) を参照してください。
まとめ
この節では、プログラム内で、JSON を生成する、JSON をパースする、JSON データを参照するなど、JSON を扱う方法について簡単に説明しました。次の節では、オブジェクト指向 JavaScript について見ていくことにします。