JSON の操作
JavaScript Object Notation (JSON) は、構造化データを表現するための標準のテキストベースの形式で、 JavaScript のオブジェクト構文に基づいています。ウェブアプリケーションでデータを転送する場合によく使われます(例えば、複数のデータをサーバーからクライアントへ送信して、ウェブページ上に表示する場合などで、その逆もあります)。頻繁に見かけるデータ形式ですので、この記事では JavaScript を使用して JSON を扱うのに必要なすべてのこと、例えば JSON を解釈してその中のデータにアクセスしたり、 JSON を作成したりする方法を説明します。
| 前提知識: | HTMLおよびCSS の基礎を理解し、これまでのレッスンで説明した JavaScript を把握していること。 |
|---|---|
| 学習成果: |
|
JSON とは何か
JSON は JavaScript オブジェクトの構文に従ったテキストベースのデータ形式です。 構造化データを文字列として表現するため、ネットワーク経由でデータを送信する際に役立ちます。 JSON は JavaScript オブジェクトの構文に似ていますが、 JavaScript とは独立して扱うことができます。多くのプログラミング言語環境には JSON を読み取ったり(解釈したり)生成したりする機能があります。
JavaScript では、JSON の解釈と生成を行うメソッドが JSON オブジェクトで提供されています。
メモ: 文字列をネイティブオブジェクトへ変換することは「デシリアライズ」 (deserialization) と呼ばれており、ネイティブオブジェクトをネットワークを通して転送できように文字列へ変換することは「シリアライズ」 (serialization) と呼ばれています。
JSON 文字列はそれ自身をファイルとして格納することもできます。 .json という拡張子の付いたただのテキストファイルで、 MIME タイプは application/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"
]
}
]
}
この JSON を JavaScript プログラムで文字列として読み込めば、通常のオブジェクトとして解釈し、JavaScript オブジェクトの基本の記事で説明したのと同じドット記法やブラケット記法を使って、その中のデータにアクセスすることができます。 例えば次のようになります。
superHeroes.homeTown;
superHeroes.members[1].powers[2];
- まず、変数名
superHeroesを指定します。 - その中の
membersプロパティへアクセスしたいので、.membersを使用します。 membersにはオブジェクトの配列が格納されています. ここでは、配列内の 2 番目のオブジェクトへアクセスするので、[1]を指定します。- そのオブジェクト内で、
powersプロパティへアクセスするため,.powersを使用します。 powersプロパティは選択したヒーローの能力を含んだ配列となっており、その中の 3 番目のものが欲しいので、[2]と記述します。
重要なポイントは、JSON を扱うこと自体に特別なことは何もないということです。JSON を JavaScript のオブジェクトとして解釈した後は、同じオブジェクトリテラル構文を使って宣言されたオブジェクトとまったく同じように扱うことができます。
メモ: 上記の JSON は JSONTest.html で参照することができます(ページ内のソースコードを参照してください)。 ページを読み込んで見て、ブラウザーのコンソールで変数内のデータにアクセスしてみてください。
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"
]
}
]
配列のアイテムにアクセスするには、(解釈済みのものの中で)配列のインデックスをまず指定します。例えば superHeroes[0].powers[0] のようにします。
JSON には単一のプリミティブ型を含めることもできます。たとえば、29、「Dan Jukes」、true はすべて有効な JSON です。
JSON 構文の制限
前述の通り、あらゆる JSON は有効な JavaScript リテラル(オブジェクト、配列、数値など)です。しかし、その逆は成り立ちません。すべての JavaScript オブジェクトリテラルが有効な JSON であるとは限りません。
- JSON にはシリアライズ可能なデータ型のみを含めることができます。つまり、
- プリミティブ型については、JSON には文字列リテラル、数値リテラル、
true、false、nullを含めることができます。なお、undefined、NaN、Infinityを含めることはできません。 - プリミティブ型以外のデータ型については、JSON にはオブジェクトリテラルや配列を含めることができますが、関数や、
Date、Set、Mapなどのその他のオブジェクト型は含めることができません。JSON 内のオブジェクトや配列は、さらに有効なJSON データ型で構成されている必要があります。
- プリミティブ型については、JSON には文字列リテラル、数値リテラル、
- 文字列は、単一引用符ではなく、二重引用符で囲む必要があります。
- 数字は 10 進法で表記する必要があります。
- オブジェクトの各プロパティは、
"キー": 値の形式で記述する必要があります。プロパティ名は、二重引用符で囲まれた文字列リテラルでなければなりません。メソッドなどの JavaScript 独自の構文は使用できません。これは、メソッドが関数であり、関数は JSON の有効なデータ型ではないためです。 - オブジェクトや配列には末尾のカンマを付けることはできません。
- コメントは JSON では許可されていません。
カンマやコロンがたった一つでも間違った位置に置かれると、JSONファイルが無効になり、処理に失敗する原因となります。 利用しようとしているデータを注意して確認してください(プログラムに問題がない限り、コンピューターが生成した JSON の方が、エラーが含まれる可能性が低くなります)。 JSONLint や JSON-validate のようなアプリケーションを使って妥当性を検証をすることもできます。
メモ: このセクションを読み終えたところで、Scrimba の JSON review MDN 学習パートナー によるインタラクティブなチュートリアルも併せて活用してみてはいかがでしょうか。このチュートリアルでは、基本的なJSON構文や、ブラウザの開発者ツール内でJSONリクエストデータを表示する方法について、役立つガイダンスが提供されています。
JSON の例を操作してみる
それでは、ウェブサイト上でどのように JSON 形式のデータを使うことができるか例を通して見てみましょう。
はじめに
まず、 heroes.html と style.css のコピーをローカルに作成してください。
後者は例題ページをスタイリングするための CSS であり、前者は簡単な HTML です。加えて、<script> 要素で、この演習で書く JavaScript コードを格納します。
<header>
...
</header>
<section>
...
</section>
<script>
// ここに JavaScript を書く
</script>
JSON データは GitHub の https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json で利用できます。
この JSON をスクリプトに読み込んで、DOM 操作を使って次のように表示することにします。

最上位の関数
最上位の関数はこんな感じです。
async function populate() {
const requestURL =
"https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json";
const request = new Request(requestURL);
const response = await fetch(request);
const superHeroes = await response.json();
populateHeader(superHeroes);
populateHeroes(superHeroes);
}
JSON を取得するには、フェッチという API を使用しています。 この API では、JavaScript を介してサーバーからリソースを取得するためのネットワークリクエストを行うことができます(画像、テキスト、JSON、HTML スニペットなど)。つまり、ページ全体を再読み込みしなくても、コンテンツの小さなセクションを更新できるのです。
この関数では、最初の 4 つの行でフェッチ API を使用して、サーバーから JSON を取得しています。
- GitHub の URL を格納するために、
requestURLという変数を宣言します。 - URL を使用して新しい
Requestオブジェクトを初期化します。 fetch()関数を使用してネットワーク要求を行い、Responseオブジェクトを返します。- レスポンスオブジェクトの
json()関数を使用して、レスポンスを JSON で取得します。
メモ:
fetch() API は非同期です。非同期関数については次のモジュールでたくさん学びますが、今は、フェッチ API を使用する関数名の前にキーワード async、あらゆる非同期関数への呼び出し前にキーワード await が必要だということだけ言っておきます。
すべて完了すると、superHeroes 変数に JSON を基にした JavaScript オブジェクトが格納されます。最初のオブジェクトは <header> を正しいデータで満たし、2 つ目はチームの各ヒーローの情報カードを作成し、それを <section> に挿入しています。
ヘッダーへの値の設定
ここまでで、JSON の取得と JavaScript オブジェクトへの変換ができました、先ほどの 2 つの関数を実装して使ってみましょう。まずはじめに、以下のコードをこれまでのコードの下に追加してください。
function populateHeader(obj) {
const header = document.querySelector("header");
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);
}
まず、h1 要素を createElement() で生成し、その textContent プロパティにそのオブジェクトの squadName プロパティを設定し、そしてそれを appendChild() で header に追加します。そして、段落についても同様に、要素を生成し、内容のテキストを設定し、 header に追加します。唯一の違いは、そのテキストがオブジェクトの homeTown と formed プロパティの両方を格納したテンプレートリテラルに設定されることです。
ヒーロー情報カードの作成
次に、以下の関数をコードの下へ追加してください。この関数はスーパーヒーローカードの作成と画面表示を行います。
function populateHeroes(obj) {
const section = document.querySelector("section");
const heroes = obj.members;
for (const hero of heroes) {
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 = hero.name;
myPara1.textContent = `Secret identity: ${hero.secretIdentity}`;
myPara2.textContent = `Age: ${hero.age}`;
myPara3.textContent = "Superpowers:";
const superPowers = hero.powers;
for (const power of superPowers) {
const listItem = document.createElement("li");
listItem.textContent = power;
myList.appendChild(listItem);
}
myArticle.appendChild(myH2);
myArticle.appendChild(myPara1);
myArticle.appendChild(myPara2);
myArticle.appendChild(myPara3);
myArticle.appendChild(myList);
section.appendChild(myArticle);
}
}
始めに、JavaScript オブジェクトの members プロパティを新しい変数に保存します。この配列には複数のオブジェクトがあり、それぞれにヒーローについての情報が入ります。
次に、for...of ループを使って配列のそれぞれのオブジェクトを反復処理します。それぞれの次のようなことを行います。
- 新しい要素をいくつか作成します。
<article>1 つ、<h2>1 つ、<p>3 つ、<ul>1 つです。 <h2>の中身を現在のヒーローの名前 (name) にします。- 3 つの段落の中身を、それぞれの
secretIdentity、age、リストにある情報を紹介していくために「超能力 ("Superpowers:")」で始まる行とします。 powersプロパティをsuperPowersという新しい定数に保存します。この定数は今のヒーローの超能力のリストを持つ配列です。- 別の
forループを使用して、今のヒーローの超能力を反復処理します。それぞれに対して<li>要素を作成し、中に超能力を入れ、listItemに<ul>要素(myList)をappendChild()で追加します。 - 最後に、
<h2>、<p>、<ul>を<article>(myArticle) の中に追加してから、その<article>を<section>の中に追加します。これらを追加する順序は重要で、これが HTML の中で表示される順序になります。
メモ: 試してみるための例が上手く取得できなかった場合は、 heroes-finished.html ソースコードを参照してみてください(こちらでライブ実行もできます)。
メモ: もし、 JavaScript オブジェクトへのアクセスに使用しているドット/ブラケット記法がよく分からない場合は、 superheroes.json を別のタブやテキストエディターで開き、それを参照しながら JavaScript を読んでみるとよいでしょう。 また、ドット記法やブラケット記法の詳細については、 JavaScript オブジェクトの基本の記事を見返してみてください。
最上位の関数の呼び出し
最後に、最上位の populate() 関数を呼び出す必要があります。
populate();
オブジェクトとテキスト間の変換
上記の例では、response.json() を使用してネットワークレスポンスを直接 JavaScript オブジェクトに変換しているので、JavaScript オブジェクトへのアクセスはシンプルでした。
しかし、時にはそれほど幸運ではないこともあります。生の JSON 文字列を受け取り、それを自分自身でオブジェクトに変換する必要がある場合もあります。また、 JavaScript のオブジェクトをネットワーク経由で送信したい場合、送信前に JSON (文字列)に変換する必要があります。幸い、この 2 つの問題はウェブ開発ではよくあることなので、ブラウザーでは組み込みの JSON オブジェクトが利用でき、それには以下の 2 つのメソッドが備わっています。
parse(): JSON 文字列を引数に取り、それに対する JavaScript オブジェクトを返します。stringify(): オブジェクトを引数に取り、等価な JSON 文字列を返します。
前者の動作例が heroes-finished-json-parse.html にあります(ソース を見て下さい)。ここでは以前に作成した例とまったく同じことをしていますが、次の部分が異なります。
- レスポンスの
text()メソッドを呼び出すことで、JSON ではなくテキストとしてレスポンスを取得する - 次に、
parse()を使用して、テキストを JavaScript オブジェクトに変換する
コードの重要な部分は以下の通りです。
async function populate() {
const requestURL =
"https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json";
const request = new Request(requestURL);
const response = await fetch(request);
const superHeroesText = await response.text();
const superHeroes = JSON.parse(superHeroesText);
populateHeader(superHeroes);
populateHeroes(superHeroes);
}
ご想像の通り、 stringify() はまったく反対の向きに動作します。次のコードをブラウザーの JavaScript コンソールに一つずつ打ち込んでいって、実際に動かしてみて下さい。
let myObj = { name: "Chris", age: 38 };
myObj;
let myString = JSON.stringify(myObj);
myString;
ここでは、JavaScript オブジェクトを作成し、その内容を確認した後、stringify() を使用して JSON 文字列に変換し、その返値を新しい変数に格納し、さらに再度確認しています。
まとめ
この記事では、プログラム内で、JSON を生成する、JSON を解釈する、JSON データを参照するなど、JSON を扱う方法について簡単に説明しました。次の記事では、これらの情報をどれだけ理解し、身についたかを確認するためのテストをいくつかご紹介します。