最終更新日: 2026-07-25
TOP(About this memo)) > 一覧(Go) > json
import (
"encoding/json"
"fmt"
)
func main() {
data := `{"name":"hoge", "age":28}`
mapData := map[string]interface{}{}
json.Unmarshal([]byte(data), &mapData)
fmt.Println(mapData["name"].(string))
}
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := `{
"result":"success",
"data": {
"entity": "aaa"
}
}`
// これだと、Dataフィールドがany型になっていて、たとえそこに具体的な構造体を代入しても、mapとして取り込みされてしまう。
respObj1 := struct {
Result string `json:"result"`
Data any `json:"data"`// ここがany型になっているのでmapとして取り込みされてしまう。
}{
Data: struct {
Entity string `json:"entity"`// リテラル内で別の構造体として入れてもmapになってしまう。
}{},
}
json.Unmarshal([]byte(data), &respObj1)
fmt.Printf("%+v \n", respObj1) // {Result:success Data:map[entity:aaa]}
// これにするとすべて構造体へ取り込みされる。
respObj2 := struct {
Result string `json:"result"`
Data struct {
Entity string `json:"entity"`
} `json:"data"`
}{}
json.Unmarshal([]byte(data), &respObj2)
fmt.Printf("%+v \n", respObj2) // {Result:success Data:{Entity:aaa}}
}
import (
"encoding/json"
"fmt"
)
type s1 struct {
F1 int `json:"f1"`
F2 string `json:"f2"`
}
func main() {
v := s1{F1: 3, F2: "aaaa"}
j, _ := json.Marshal(&v)// 戻り値は[]byte型。
fmt.Println("%+v", string(j))
}
omitempty “オプションは、フィールドが空の値(false、0、nilポインタ、nilインターフェース値、長さ0の配列、スライス、マップ、文字列として定義される)を持つ場合、そのフィールドをエンコードから省略することを指定する。 特別なケースとして、フィールドタグが”-“の場合、フィールドは常に省略される。 名前”-“のフィールドは、タグ”-, “を使っても生成できることに注意。
type Person struct {
Name string
Age int
}
...
jsonReader := strings.NewReader(`{"Name":"Alice","Age":20}`)
jsonDecoder := json.NewDecoder(jsonReader)
var p Person
err := jsonDecoder.Decode(&p)
if err != nil {
log.Fatal(err)
}
fmt.Println(p)
v := s1{F1: 3, F2: "aaaa"}
buffer := &bytes.Buffer{}
err := json.NewEncoder(buffer).Encode(v)
// err := json.NewEncoder(os.Stdout).Encode(stcData)// 標準出力の場合
if err != nil {
fmt.Println(err)
}
fmt.Println(buffer.String())
var str string
if err := json.Unmarshal([]byte("\"\n\""), &str); err != nil {
fmt.Print(err)// &json.SyntaxError{msg:"invalid character '\\n' in string literal",...}
}
import (
"encoding/json"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/google/uuid"
)
func GetFirst[A any](a A, b ...any) A {
return a
}
func GetLast(a ...any) any {
return a[len(a)-1]
}
func UnmarshalString[S any](jsn string, to *S) (*S, error) {
if err := json.Unmarshal([]byte(jsn), to); err != nil {
return nil, err
}
return to, nil
}
func Ptr[T any](a T) *T {
return &a
}
func TestJson(t *testing.T) {
t.Run("assert_json", func(t *testing.T) {
assert.Exactly(t, string(GetFirst(json.Marshal("aaaaaa"))), `"aaaaaa"`)
assert.Exactly(t, *GetFirst(UnmarshalString(`{"number":1}`, &struct {
Number *int `json:"number"`
}{})), struct {
Number *int `json:"number"`
}{
Number: Ptr(1),
})
assert.Exactly(t, *GetFirst(UnmarshalString(`{"number":0}`, &struct {
Number *int `json:"number"`
}{})), struct {
Number *int `json:"number"`
}{
Number: Ptr(0),
})
assert.Exactly(t, *GetFirst(UnmarshalString(`{}`, &struct {
Number *int `json:"number"`
}{})), struct {
Number *int `json:"number"`
}{
Number: nil,
})
assert.Exactly(t, *GetFirst(UnmarshalString(`{"created_at":"2023-05-04T00:00:00.000000Z"}`, &struct {
CreatedAt *time.Time `json:"created_at"`
}{})), struct {
CreatedAt *time.Time `json:"created_at"`
}{
CreatedAt: Ptr(GetFirst(time.Parse("2006-01-02T15:04:05.000000Z07:00", "2023-05-04T00:00:00.000000Z"))),
})
assert.Exactly(t, *GetFirst(UnmarshalString(`{}`, &struct {
CreatedAt *time.Time `json:"created_at"`
}{})), struct {
CreatedAt *time.Time `json:"created_at"`
}{
CreatedAt: nil,
})
// json側がnullの場合は ゼロ値として値が入る。
assert.Exactly(t, GetFirst(UnmarshalString(`{"ID":null}`, &struct{ ID string }{})), &struct{ ID string }{})
assert.Exactly(t, GetFirst(UnmarshalString(`{"ID":null}`, &struct{ ID time.Time }{})), &struct{ ID time.Time }{})
assert.Exactly(t, *GetFirst(UnmarshalString(`null`, Ptr(""))), "")
assert.Exactly(t, GetLast(UnmarshalString(`{"ID":3}`, &struct{ ID int }{})), nil)
assert.NotEqual(t, GetLast(UnmarshalString(`{"ID":"aaaa"}`, &struct{ ID int }{})), nil)
assert.Exactly(t, GetLast(UnmarshalString(`{"ID":3}`, &struct{ ID uint }{})), nil)
// json側が空文字の場合は、型が一致しないとエラーになる。
assert.Exactly(t, GetFirst(UnmarshalString(`{"ID":""}`, &struct{ ID string }{})), &struct{ ID string }{})
assert.NotEqual(t, GetLast(UnmarshalString(`{"ID":""}`, &struct{ ID int }{})), nil)
assert.NotEqual(t, GetLast(UnmarshalString(`{"ID":""}`, &struct{ ID time.Time }{})), nil) // 日時としてparseできずにエラー
assert.NotEqual(t, GetLast(UnmarshalString(`""`, &time.Time{})), nil) // 日時としてparseできずにエラー
/// stringの場合はOK
assert.Exactly(t, GetLast(UnmarshalString(`{"ID":""}`, &struct{ ID string }{})), nil)
assert.Exactly(t, *GetFirst(UnmarshalString(`""`, Ptr(""))), "")
// 何も入っていないtime.Timeはデフォルトが0001-01-01T00:00:00Z
assert.Exactly(t, string(GetFirst(json.Marshal(time.Time{}))), "\"0001-01-01T00:00:00Z\"")
// uuid.UUIDのデフォルト値は00000000-0000-0000-0000-000000000000
assert.Exactly(t, string(GetFirst(json.Marshal(uuid.UUID{}))), "\"00000000-0000-0000-0000-000000000000\"")
assert.Exactly(t, string(GetFirst(json.Marshal(struct{ ID uuid.UUID }{}))), "{\"ID\":\"00000000-0000-0000-0000-000000000000\"}")
})
}
import (
"bytes"
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func GetFirst[A any](a A, b ...any) A {
return a
}
func Ptr[T any](a T) *T {
return &a
}
func TestJsonEscape(t *testing.T) {
t.Run("assert_Json_escape_by_unmarshal/marshal", func(t *testing.T) {
// json.Unmarshalの挙動は以下。
var str string
assert.NotNil(t, json.Unmarshal([]byte(`"""`), &str)) // これはエラー。
assert.Nil(t, json.Unmarshal([]byte(`"\""`), &str)) // エスケープさえすればOKなようだ。
assert.NotNil(t, json.Unmarshal([]byte(`"\"`), &str)) // \に続くエスケープシーケンスを表す文字がないためエラーになる。
assert.NotNil(t, json.Unmarshal([]byte(`"\p"`), &str)) // 「\p」は存在しないエスケープシーケンスのためエラーになる。
assert.Nil(t, json.Unmarshal([]byte(`"/"`), &str)) // スラッシュもエラーにならない。
assert.Nil(t, json.Unmarshal([]byte(`"><&"`), &str)) // HTML文字が入っててもエラーにはならない。
assert.Equal(t, str, `><&`)
assert.Nil(t, json.Unmarshal([]byte(`"`+"\u2028"+`"`), &str))
assert.Equal(t, str, "\u2028")
assert.Nil(t, json.Unmarshal([]byte(`"`+"\u2029"+`"`), &str))
assert.Equal(t, str, "\u2029")
assert.Nil(t, json.Unmarshal([]byte(`"`+`\u003c`+`"`), &str)) // 一方、HTML文字を表すunicode文字列はHTML文字へ変換される。
assert.Equal(t, str, `<`)
assert.Nil(t, json.Unmarshal([]byte(`"`+`\u003e`+`"`), &str))
assert.Equal(t, str, `>`)
assert.Nil(t, json.Unmarshal([]byte(`"`+`\u0026`+`"`), &str))
assert.Equal(t, str, `&`)
assert.Nil(t, json.Unmarshal([]byte(`"`+`\u2028`+`"`), &str))
assert.Equal(t, str, "\u2028")
assert.Nil(t, json.Unmarshal([]byte(`"`+`\u2029`+`"`), &str))
assert.Equal(t, str, "\u2029")
assert.NotNil(t, json.Unmarshal([]byte(`"`+"\n"+`"`), &str)) // 特殊文字はエラー。
assert.Equal(t, json.Valid([]byte(`"`+"\n"+`"`)), false) // 特殊文字はJsonとしてValidではない。(cf) jqコマンドでも例えば、 echo '"\n"' | jq のように実行するとパースエラーになる)
assert.NotNil(t, json.Unmarshal([]byte("\"\u0000\""), &str))
assert.NotNil(t, json.Unmarshal([]byte("\"\u0004\""), &str))
assert.NotNil(t, json.Unmarshal([]byte("\"\u0019\""), &str))
assert.NotNil(t, json.Unmarshal([]byte("\"\b\""), &str))
assert.NotNil(t, json.Unmarshal([]byte("\"\t\""), &str))
assert.NotNil(t, json.Unmarshal([]byte("\"\f\""), &str))
assert.Nil(t, json.Unmarshal([]byte(`"\u0000\u0001\u0002\u0003\u0004\b\t\n\f\r\u0019あ"`), &str)) // エスケープシーケンスは特殊文字へ変換される。
assert.Equal(t, str, "\u0000\u0001\u0002\u0003\u0004\b\t\n\f\r\u0019あ")
assert.Nil(t, json.Unmarshal([]byte(`"`+`\\n\"`+`"`), &str)) // エスケープされているものはその文字として解釈される。
assert.Equal(t, str, `\n"`)
assert.Nil(t, json.Unmarshal([]byte(`"`+"\xe3\x81\x82"+`"`), &str))
assert.Equal(t, str, `あ`)
assert.Nil(t, json.Unmarshal([]byte(`"`+"\xe3\x81"+`"`), &str)) // unmarshalのコメントにあるように、UTF-8として無効な文字が含まれている場合はエラーにならずにU+FFFDへ変換される。
assert.Equal(t, str, "\uFFFD\uFFFD")
// json.Marshal関数はエスケープシーケンスが含まれている場合、エスケープする。
// そして、特殊文字はエスケープシーケンスへ変換される。
//(これはオプションなどで回避することはできない。json.NewEncoderを使えば可能。)
// (外側の「"」はjsonの文字列として自動的に付与される)
assert.Equal(t, string(GetFirst(json.Marshal(`\n`))), `"`+`\\n`+`"`) // 「\n」は 「\\n」に変換される。
assert.Equal(t, string(GetFirst(json.Marshal(`"`))), `"`+`\"`+`"`) // 「"」は 「\"」に変換される。
assert.Equal(t, string(GetFirst(json.Marshal(`/`))), `"`+`/`+`"`) // スラッシュはエスケープされない。
assert.Equal(t, string(GetFirst(json.Marshal(`"\\b\t\n\f\r`))), `"`+`\"\\\\b\\t\\n\\f\\r`+`"`)
assert.Equal(t, string(GetFirst(json.Marshal("\u0000\u0001\u0002\u0003\u0004\u0008\u0009\u000A\u000C\u000D\u0019\u0020\u0021"))), `"`+`\u0000\u0001\u0002\u0003\u0004\u0008\t\n\u000c\r\u0019 !`+`"`) // 特殊文字はエスケープシーケンスへ変換される。(\u0008, \u000Cは、 \b, \f にはならないようだ。)
// なお、Marshal関数のコメントにあるように、上記に加えて、
// HTML文字("<", ">", "&", U+2028(LS), and U+2029(PS))はunicodeの文字列へ変換される。
assert.Equal(t, string(GetFirst(json.Marshal(`<`))), `"`+`\u003c`+`"`)
assert.Equal(t, string(GetFirst(json.Marshal(`>`))), `"`+`\u003e`+`"`)
assert.Equal(t, string(GetFirst(json.Marshal(`&`))), `"`+`\u0026`+`"`)
assert.Equal(t, string(GetFirst(json.Marshal("\u2028"))), `"`+`\u2028`+`"`)
assert.Equal(t, string(GetFirst(json.Marshal("\u2029"))), `"`+`\u2029`+`"`)
})
t.Run("assert_Json_escape_by_decoder/encoder", func(t *testing.T) {
//var result map[string]interface{}
var result string
// Unmarshalと同様にエスケープシーケンスは特殊文字へ変換される。(回避不可能?)
assert.Nil(t, json.NewDecoder(bytes.NewReader([]byte(`"\b\f\n\r\tあ"`))).Decode(&result))
assert.Equal(t, result, "\b\f\n\r\tあ")
// これはUnmarshalと違ってエラーにならない。(削除される)
assert.Nil(t, json.NewDecoder(bytes.NewReader([]byte(`"""`))).Decode(&result))
assert.Equal(t, result, "")
// 以下はUnmarshalと同様にエラー
assert.NotNil(t, json.NewDecoder(bytes.NewReader([]byte(`"\"`))).Decode(&result))
assert.NotNil(t, json.NewDecoder(bytes.NewReader([]byte(`"\p"`))).Decode(&result))
assert.Nil(t, json.NewDecoder(bytes.NewReader([]byte(`"><&"`))).Decode(&result))
assert.Equal(t, result, `><&`)
assert.NotNil(t, json.NewDecoder(bytes.NewReader([]byte("\"\n\""))).Decode(&result))
// Unmarshalと同様にHTML文字に対応するunicode文字列をHTML文字へ変換される。
assert.Nil(t, json.NewDecoder(bytes.NewReader([]byte(`"`+`\u003c`+`"`))).Decode(&result))
assert.Equal(t, result, `<`)
// Encoderは仕様として末尾に"\n"が設定される。(この挙動の事由は分からなかった。)
// それ以外の挙動はMarshalと同じようだ。
// https://stackoverflow.com/questions/36319918/why-does-json-encoder-add-an-extra-line
resultB := bytes.Buffer{}
//enc.SetEscapeHTML(false)
assert.Nil(t, json.NewEncoder(&resultB).Encode(`\n`))
assert.Equal(t, resultB.String(), `"`+`\\n`+`"`+"\n")
resultB = bytes.Buffer{}
assert.Nil(t, json.NewEncoder(&resultB).Encode("\n"))
assert.Equal(t, resultB.String(), `"`+`\n`+`"`+"\n")
resultB = bytes.Buffer{}
assert.Nil(t, json.NewEncoder(&resultB).Encode(`"\\b\f\n\r\t`))
assert.Equal(t, resultB.String(), `"`+`\"\\\\b\\f\\n\\r\\t`+`"`+"\n")
resultB = bytes.Buffer{}
assert.Nil(t, json.NewEncoder(&resultB).Encode(`<`))
assert.Equal(t, resultB.String(), `"`+`\u003c`+`"`+"\n")
// ただし、SetEscapeHTMLをfalseで設定すると、HTML文字の変換はされないようだ。
resultB = bytes.Buffer{}
en := json.NewEncoder(&resultB)
en.SetEscapeHTML(false)
assert.Nil(t, en.Encode(`<`))
assert.Equal(t, resultB.String(), `"`+`<`+`"`+"\n")
})
}