Golang PR

Golang(Go言語) – JSONをParseする方法【完全ガイド】

記事内に商品プロモーションを含む場合があります

本記事では
Golang(Go言語)での
JSONパースについて解説します!

JSONとGoについて

JSON(JavaScript Object Notation)は、
データをやり取りする際によく使われる軽量なデータフォーマットです。

APIやWebサービスとの通信で頻繁に使われるため、
扱い方を知っておくと便利です。

Go言語には、
JSONを簡単に扱うための
標準ライブラリが備わっているので、
これを使えば初心者でもスムーズにJSONをパースできます。

encoding/jsonパッケージの基礎

GoでJSONを扱うために必要なのが、
標準ライブラリのencoding/jsonパッケージです。

これを使うと、
文字列として表現されたJSONデータを
Go言語のデータ型に変換(パース)することができます。

Goの標準ライブラリには
ほとんどのケースに対応する機能が揃っているので、
まずはこのライブラリの使い方を理解しましょう。

JSONパースの基本的な実装方法

簡単な例:JSONをGoの構造体に変換

まず、基本的な例から始めましょう。
以下のようなJSONデータがあったとします。

{
  "名前": "田中",
  "年齢": 30
}


このデータをGoで扱うには、
次のような構造体を定義し、
json.Unmarshalという関数を使って変換します。

type Person struct {
    Name string `json:"名前"`
    Age  int    `json:"年齢"`
}


次に、この構造体に対して
JSONをパースするコードを書きます。

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string `json:"名前"`
    Age  int    `json:"年齢"`
}

func main() {
    // パースするJSONデータ
    jsonData := `{
        "名前": "田中",
        "年齢": 30
    }`

    // 構造体の変数を宣言
    var person Person

    // JSONを構造体にパース
    err := json.Unmarshal([]byte(jsonData), &person)
    if err != nil {
        fmt.Println("エラー:", err)
        return
    }

    // 結果を表示
    fmt.Printf("名前: %s, 年齢: %d\n", person.Name, person.Age)
}


このように、
構造体を使ってデータを整理することで、
コードが読みやすくなります。

構造体を使ってJSONをパースする方法

構造体を使うことで、
JSONの各フィールドを対応する型にマッピングできます。
例えば、文字列はGoのstring型、数字はint型に変換されます。

異なる型のデータを扱う

JSONデータでは、
型が異なるデータが混在することがあります。

例えば、
あるフィールドでは数値が使われている場合でも、
他のデータでは文字列として扱われることがあります。

Goでは、
型が異なるデータを扱うためにinterface{}型を使います。
interface{}は「何でも受け取れる型」と考えるとわかりやすいです。

type Data struct {
    Name   string      `json:"名前"`
    Age   interface{} `json:"年齢"`
}


このようにすれば、
年齢が数値でも文字列でも問題なく処理できます。

構造体を使わずにJSONをパースする方法

構造体を定義せずに、
直接map[string]interface{}
使ってJSONをパースすることも可能です。

この方法では、
より柔軟にJSONを扱えますが、
データの型に注意する必要があります。

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    // パースするJSONデータ
    jsonData := `{
        "名前": "田中",
        "年齢": 30
    }`

    // mapを使ってJSONデータを受け取る
    var data map[string]interface{}

    // JSONをパース
    err := json.Unmarshal([]byte(jsonData), &data)
    if err != nil {
        fmt.Println("エラー:", err)
        return
    }

    // 結果を表示
    name, ok := data["名前"].(string)
    if ok {
        fmt.Printf("名前: %s\n", name)
    } else {
        fmt.Println("名前の取得に失敗しました")
    }

    age, ok := data["年齢"].(float64) // JSONでは数値はfloat64でパースされる
    if ok {
        fmt.Printf("年齢: %.0f\n", age)
    } else {
        fmt.Println("年齢の取得に失敗しました")
    }
}


この場合、
data["名前"]data["年齢"]の値を使う際に、
型アサーション(型を明確にする作業)が必要です。

解説

  1. JSONデータをmap[string]interface{}にパースします。
    これは、キーがstring、値が任意の型(interface{})を持つマップです。
  2. パースしたデータから値を取り出す際、
    型アサーションを使って値を明示的にキャストします。

    たとえば、名前は文字列(string)、年齢は数値ですが、
    Goでは数値はfloat64としてパースされるので、
    型アサーションを使ってfloat64型として扱います。
  3. 最後に、取得したデータを適切に表示します。

配列やネスト構造のJSONを扱う

コード例

JSONには配列も含まれることがあります。
例えば、次のようなデータです。

{
	"プロジェクト": {
		"名前": "Goプロジェクト",
		"チーム": {
			"リーダー": "田中",
			"メンバー": [
				{
					"名前": "鈴木",
					"年齢": 25
				},
				{
					"名前": "佐藤",
					"年齢": 28
				}
			]
		}
	}
}


配列やネスト構造のJSONを扱う場合も、
構造体を使わずにmap[]interface{}を使って柔軟に対応できます。

次に、配列とネストされたJSONデータを
パースする動作するコードを示します。

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    // パースするJSONデータ
    jsonData := `{
        "プロジェクト": {
            "名前": "Goプロジェクト",
            "チーム": {
                "リーダー": "田中",
                "メンバー": [
                    {"名前": "鈴木", "年齢": 25},
                    {"名前": "佐藤", "年齢": 28}
                ]
            }
        }
    }`

    // mapを使ってJSONデータを受け取る
    var data map[string]interface{}

    // JSONをパース
    err := json.Unmarshal([]byte(jsonData), &data)
    if err != nil {
        fmt.Println("エラー:", err)
        return
    }

    // ネストされたデータを処理
    project, ok := data["プロジェクト"].(map[string]interface{})
    if !ok {
        fmt.Println("プロジェクトの取得に失敗しました")
        return
    }

    projectName := project["名前"].(string)
    fmt.Printf("プロジェクト名: %s\n", projectName)

    // チームデータを処理
    team, ok := project["チーム"].(map[string]interface{})
    if !ok {
        fmt.Println("チームデータの取得に失敗しました")
        return
    }

    leader := team["リーダー"].(string)
    fmt.Printf("チームリーダー: %s\n", leader)

    // 配列データを処理
    members, ok := team["メンバー"].([]interface{})
    if !ok {
        fmt.Println("メンバーデータの取得に失敗しました")
        return
    }

    // 配列の各要素にアクセス
    for i, member := range members {
        memberMap, ok := member.(map[string]interface{})
        if !ok {
            fmt.Printf("メンバー%dの取得に失敗しました\n", i+1)
            continue
        }
        name := memberMap["名前"].(string)
        age := memberMap["年齢"].(float64) // JSONの数値はfloat64で扱われる
        fmt.Printf("メンバー%d: 名前=%s, 年齢=%.0f\n", i+1, name, age)
    }
}

解説

  1. このコードでは、プロジェクトの名前、
    チームのリーダー、メンバーの配列(メンバー名と年齢)を扱っています。
  2. JSON内の配列をGoで扱う場合、
    配列は[]interface{}型としてパースされます。
    それをforループを使って各要素にアクセスしています。
  3. 配列内の各要素は、
    map[string]interface{}としてキャストし、
    それぞれのフィールド(名前、年齢)にアクセスしています。
  4. Goでは、JSONの数値はfloat64として扱われるので、
    年齢を取得するときはfloat64型に型アサーションを行っています。

サードパーティライブラリの活用

Goには標準ライブラリ以外にも、
サードパーティ製の便利なJSONライブラリがいくつか存在します。
ここでは、代表的な3つのライブラリを紹介します。

antonholmquist/jason

antonholmquist/jasonは、
構造体を定義せずに簡単にJSONを
パースできるライブラリです。
特定のキーにアクセスする際や
JSONをより柔軟に扱いたい場合に便利です。
特に、ネストされた構造の処理が簡単になります。

package main

import (
    "fmt"
    "github.com/antonholmquist/jason"
)

func main() {
    jsonData := `{
        "プロジェクト": {
            "名前": "Goプロジェクト",
            "チーム": {
                "リーダー": "田中"
            }
        }
    }`

    v, err := jason.NewObjectFromBytes([]byte(jsonData))
    if err != nil {
        fmt.Println("エラー:", err)
        return
    }

    leader, _ := v.GetString("プロジェクト", "チーム", "リーダー")
    fmt.Println("チームリーダー:", leader)
}

buger/jsonparser

buger/jsonparserは、
大規模なデータや構造が不明な
JSONデータを扱うのに適したライブラリです。
特定のフィールドに素早くアクセスできます。

buger/jsonparserは、
非常に高速で軽量なJSONパーサーです。
大規模なデータを扱う際に役立ちます。

package main

import (
    "fmt"
    "github.com/buger/jsonparser"
)

func main() {
    jsonData := `{
        "プロジェクト": {
            "チーム": {
                "リーダー": "田中"
            }
        }
    }`

    leader, err := jsonparser.GetString([]byte(jsonData), "プロジェクト", "チーム", "リーダー")
    if err != nil {
        fmt.Println("エラー:", err)
        return
    }

    fmt.Println("チームリーダー:", leader)
}

tidwall/gjson

tidwall/gjsonは、
ドット表記で直感的にJSONデータを
扱えるライブラリで、
特に複雑なネスト構造を持つJSONに対して非常に便利です。
構文が直感的で使いやすいため、
パースが非常に楽になります。

package main

import (
    "fmt"
    "github.com/tidwall/gjson"
)

func main() {
    jsonData := `{
        "プロジェクト": {
            "チーム": {
                "リーダー": "田中"
            }
        }
    }`

    leader := gjson.Get(jsonData, "プロジェクト.チーム.リーダー").String()
    fmt.Println("チームリーダー:", leader)
}

パフォーマンスに関する考察

サードパーティライブラリには、
それぞれ特有のメリットがありますが、
gjsonは特にパフォーマンスが高いことで知られています。

大規模なデータを迅速に処理する必要がある場合や、
複雑なネスト構造に対応する際に、
gjsonは非常に効果的です。

また、jsonparserも軽量で高速なパーサーであり、
リソースを効率的に使いたい場合に適しています。

エラーハンドリングとベストプラクティス

JSONパース時には、
データが期待通りでないこともあります。

そんな時には、エラーハンドリングが重要です。
Goでは、json.Unmarshalの返り値であるエラーをチェックし、
エラーが発生した場合に適切な処理を行います。

また、
JSONを扱う際のベストプラクティスとして、
以下のポイントを押さえておくと良いでしょう。

  • 構造体を使ってデータを整理する
  • 必要なフィールドだけをパースする
  • 型が異なる場合はinterface{}を使用する

まとめ

この記事では、
GoでJSONをパースする基本的な方法から、
構造体の使い方、サードパーティライブラリの活用までを解説しました。

JSONのパースは最初は難しく感じるかもしれませんが、
ポイントを押さえれば効率よくデータを扱えるようになります。

Go言語の強力な標準ライブラリを使って、
ぜひ実際のプロジェクトでJSONを扱ってみてください!