Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 19341] ファイルの概要

このコミットは、Go言語の標準ライブラリであるencoding/jsonパッケージ内のsrc/pkg/encoding/json/decode.goファイルに対する変更です。このファイルは、JSONデータをGoのデータ構造にデコード(アンマーシャル)する際のロジックを実装しています。具体的には、JSONのnull値がGoの非参照型にアンマーシャルされる際の挙動に関するドキュメントが追加されています。

コミット

このコミットは、encoding/jsonパッケージにおいて、JSONのnull値をGoの非参照型(ポインタ、インターフェース、マップ、スライス以外の型)にアンマーシャルする際の挙動を明確にするためのドキュメントを追加します。これは、Go 1.1以降の既存の挙動を文書化し、関連するIssue #6724を修正することを目的としています。コミットメッセージによると、この挙動は当初の設計意図とは異なり、Issue #2540での議論の結果として採用されたものであり、Go 1.1以降の互換性のために維持されています。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/fc1e5a8acdfba04482bb17b4ad5bbd3778c50fb3

元コミット内容

commit fc1e5a8acdfba04482bb17b4ad5bbd3778c50fb3
Author: Russ Cox <rsc@golang.org>
Date:   Mon May 12 23:38:26 2014 -0400

    encoding/json: document what unmarshal of `null` into non-reference type does
    
    Originally it was an error, which made perfect sense, but in issue 2540
    I got talked out of this sensible behavior. I'm not thrilled with the "new"
    behavior but it's been there since Go 1.1 so we're stuck with it now.
    
    Fixes #6724.
    
    LGTM=bradfitz
    R=golang-codereviews, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/100430043

変更の背景

Goのencoding/jsonパッケージにおけるJSONのnull値のアンマーシャル挙動は、開発者にとって混乱の元となることがありました。特に、JSONのnullをGoのプリミティブ型(例:int, string, bool)や構造体などの非参照型にアンマーシャルしようとした場合の挙動が不明確でした。

コミットメッセージによると、元々はJSONのnullを非参照型にアンマーシャルしようとするとエラーになるのが「完璧に理にかなった」挙動であると考えられていました。しかし、Issue #2540での議論の結果、この「理にかなった」挙動は採用されず、現在の挙動(非参照型へのnullのアンマーシャルは値に影響を与えず、エラーも発生しない)がGo 1.1から導入され、そのまま維持されてきました。

このコミットは、この既存の挙動を明示的にドキュメント化することで、開発者の混乱を解消し、Issue #6724で報告されたドキュメントの不足を修正することを目的としています。つまり、コードの動作自体を変更するのではなく、その動作を明確に説明するためのコメントを追加しています。

前提知識の解説

JSONのnull

JSON (JavaScript Object Notation) におけるnullは、特定のデータ型を持たない特殊な値です。これは通常、値の不在、不明、または適用不能であることを示します。プログラミング言語によっては、nullはポインタのゼロ値や、オブジェクトが存在しないことを示すために使用されます。

Goのnil

Go言語におけるnilは、以下の型のゼロ値として使用されます。

  • ポインタ (*T)
  • インターフェース (interface{})
  • マップ (map[K]V)
  • スライス ([]T)
  • チャネル (chan T)
  • 関数 (func())

nilは、これらの型が何も参照していない状態を示します。GoにはJSONのnullに直接対応するプリミティブなnull型は存在しません。

encoding/jsonパッケージ

encoding/jsonは、Goの標準ライブラリに含まれるパッケージで、Goのデータ構造とJSONデータの間で変換(エンコードとデコード)を行う機能を提供します。

  • エンコード (Marshal): Goのデータ構造をJSONデータに変換します。
  • デコード (Unmarshal): JSONデータをGoのデータ構造に変換します。

json.Unmarshal関数

json.Unmarshal(data []byte, v interface{}) errorは、JSON形式のバイトスライスdataをGoの値vにデコードする関数です。vはポインタである必要があります。この関数は、JSONデータとGoのデータ構造の間のマッピングを自動的に行いますが、その挙動はGoの型の種類によって異なります。

参照型と非参照型

Goの型は大きく「参照型」と「非参照型」に分けられます。

  • 参照型: ポインタ、インターフェース、マップ、スライスなど、メモリ上の他の場所を指し示す型です。これらの型はnilになり得ます。JSONのnullは、これらのGoの参照型にアンマーシャルされると、対応するGoの値をnilに設定します。
  • 非参照型: プリミティブ型(int, string, bool, floatなど)、構造体、配列など、値そのものを保持する型です。これらの型はnilになりません。JSONのnullをこれらの型にアンマーシャルする際の挙動が、このコミットで明確化された点です。

技術的詳細

このコミットが文書化している主要な技術的詳細は、json.UnmarshalがJSONのnull値をGoの非参照型にアンマーシャルする際の挙動です。

  1. 参照型へのアンマーシャル: JSONのnull値がGoのインターフェース、マップ、ポインタ、またはスライスにアンマーシャルされる場合、json.Unmarshalは対応するGoの値をnilに設定します。これは、JSONのnullがGoの参照型の「値の不在」を表現する自然な方法であるためです。

  2. 非参照型へのアンマーシャル(このコミットの焦点): JSONのnull値がGoのその他の型(例:string, int, bool, float64, 構造体など)にアンマーシャルされる場合、json.Unmarshalは以下の挙動を示します。

    • Goの値には何の影響も与えない: ターゲットとなるGoの変数の現在の値は変更されません。例えば、int型の変数に10が設定されている状態でJSONのnullをアンマーシャルしても、その変数の値は10のままです。
    • エラーは発生しない: この操作はエラーとは見なされず、UnmarshalTypeErrorなどのエラーも返されません。

この挙動の背景には、「nullはJSONにおいてしばしば『存在しない』ことを意味する」という考え方があります。つまり、JSONデータに特定のフィールドがnullとして存在する場合、それはそのフィールドの値が「存在しない」ことを示し、Goの対応する非参照型変数に値を設定する必要がない、と解釈されます。

この挙動は、Go 1.1から存在しており、コミットメッセージにあるように、当初の設計者の意図(エラーを返す)とは異なるものの、後方互換性のために維持されています。このコミットは、この既存の、しかし直感的ではないかもしれない挙動を、encoding/jsonパッケージのドキュメントに明示的に追加することで、開発者が予期せぬ動作に遭遇するのを防ぐことを目的としています。

コアとなるコードの変更箇所

--- a/src/pkg/encoding/json/decode.go
+++ b/src/pkg/encoding/json/decode.go
@@ -54,6 +54,11 @@ import (
 // If no more serious errors are encountered, Unmarshal returns
 // an UnmarshalTypeError describing the earliest such error.
 //
+// The JSON null value unmarshals into an interface, map, pointer, or slice
+// by setting that Go value to nil. Because null is often used in JSON to mean
+// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
+// on the value and produces no error.
+//
 // When unmarshaling quoted strings, invalid UTF-8 or
 // invalid UTF-16 surrogate pairs are not treated as an error.
 // Instead, they are replaced by the Unicode replacement

コアとなるコードの解説

変更はsrc/pkg/encoding/json/decode.goファイル内のコメントの追加のみです。

追加された5行のコメントは、json.Unmarshal関数のドキュメントの一部として、JSONのnull値がGoの異なる型にアンマーシャルされる際の具体的な挙動を説明しています。

// The JSON null value unmarshals into an interface, map, pointer, or slice
// by setting that Go value to nil. Because null is often used in JSON to mean
// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
// on the value and produces no error.

このコメントは以下の点を明確にしています。

  1. 参照型へのnullのアンマーシャル: interfacemappointer、またはsliceといったGoの参照型にJSONのnullがアンマーシャルされる場合、対応するGoの値はnilに設定されます。これは、Goの参照型がnilをサポートしているため、JSONのnullを自然に表現できます。

  2. 非参照型へのnullのアンマーシャル: 「nullはJSONにおいてしばしば『存在しない』ことを意味する」という理由から、JSONのnullその他のGoの型(つまり、参照型ではないプリミティブ型や構造体など)にアンマーシャルしても、そのGoの値には何の影響も与えずエラーも発生しません。これは、JSONのnullがそのフィールドが「存在しない」ことを示唆しているため、Goの変数に値を設定する必要がない、という解釈に基づいています。

このコメントの追加により、json.Unmarshalのドキュメントがより包括的になり、開発者がJSONのnullをGoの非参照型にアンマーシャルする際の予期せぬ挙動について事前に理解できるようになります。

関連リンク

参考にした情報源リンク

  • Go encoding/json パッケージドキュメント: https://pkg.go.dev/encoding/json
  • Go GitHub Issues (特に #2540, #6724 の議論)
  • Stack Overflow や Go コミュニティでの encoding/jsonnull 処理に関する議論