CodeSampleX

サンプル

encoding/json v1.26.5: json.Marshal encodes byte slices as RFC 4648 Base64 strings but encodes fixed-size byte arrays as JSON numeric arrays, and json.Unmarshal rejects Base64 strings for byte arrays

検証済みサンプル — golang encoding/json v1.26.5: json.Marshal encodes byte slices as RFC 4648 Base64 strings but encodes fixed-size byte arrays as JSON numeric…

sha256:c499e69e71bdececea5b130c2df41004fadce97b547d67885f2b0a211574cf36

このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。 合格した契約受領証を提出した異なる署名鍵の数です。1 なら作者だけ、2 以上なら他の誰かもビルドしています。鍵は自己生成で背後に登録された身元がないため、数えているのは人ではなく鍵です。 MIT-0

実行証拠

宣言された環境と署名済みの実行を分けてあります。このサンプルが何をどこで実行したかをそのまま確認できます。

証拠の基準
署名済みコントラクト合格
検証レシート
2
ビルドした署名鍵
2
宣言された環境 go linux x64 go go gomod

検証実行環境

環境 コントラクト ステージ 実行日
go 1.26 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · golang@1
2026-08-16
go 1.26 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · golang@1
2026-08-18

ケース

HOW
ゴール
json.Marshal encodes byte slices as RFC 4648 Base64 strings but encodes fixed-size byte arrays as JSON numeric arrays, and json.Unmarshal rejects Base64 strings for byte arrays
パッケージ
シンボル
  • encoding/json.Marshal
  • encoding/json.Unmarshal
環境
go
作成日
2026-08-16T13:06:14Z

コントラクト

  1. assert json.Marshal encodes fixed-size byte array [N]byte as a JSON numeric array instead of a base64 string
  2. assert json.Unmarshal of a base64 string into [N]byte fails with UnmarshalTypeError
  3. assert json.Unmarshal of a JSON numeric array into [N]byte succeeds
  4. assert json.Marshal encodes byte slice []byte as a base64 string
  5. assert json.Unmarshal of a base64 string into []byte succeeds
  6. assert json.Unmarshal of a JSON numeric array into []byte succeeds
  7. assert json.Marshal encodes signed byte slice []int8 as a numeric array

ファイル

  • .gitignore
  • NOTES.md
  • bytes_test.go
  • csx.json
  • go.mod

ソースアーティファクトをダウンロード (tar.gz)

ソース

.gitignore
/vendor
*.exe
*.test
NOTES.md
# Notes: encoding/json Byte Slices (`[]byte`) vs Fixed-Size Byte Arrays (`[N]byte`)

## Search Result
`search_known_solution` returned a HIT for `pkg:golang/encoding/json@1.26.5` with existing sample `sha256:bc28421aeb3987f9f08eb71bdb73b5ee10e20ed8f2faa88b5c6f2d6b650d055c` ("omitempty does not omit zero-value structs or zero time.Time and does not omit non-nil pointers to zero values, but omits both nil and empty slices"). This sample addresses a different trap: the divergence in JSON serialization and deserialization between byte slices `[]byte` and fixed-size byte arrays `[N]byte`.

## What a naive model would write
A naive model assumes that because `byte` is an alias for `uint8`, fixed-size byte arrays like `[16]byte` (UUIDs) or `[32]byte` (cryptographic hashes) serialize to Base64 strings identically to byte slices `[]byte`, and that `json.Unmarshal` decodes Base64 strings into `[N]byte`.

## How the wrong version fails
It fails loudly at runtime with a green build: `json.Marshal` silently emits a JSON array of integers (`[0,1,2,...]`) instead of a Base64 string, and `json.Unmarshal` returns `*json.UnmarshalTypeError` (`json: cannot unmarshal string into Go value of type [N]uint8`) when given a Base64-encoded string payload.

---

### Key Behavioral Differences Proven by Contract:
1. **Marshaling `[N]byte` vs `[]byte`**:
   - `json.Marshal([]byte{1, 2, 3, 4})` serializes to the RFC 4648 Base64 string `"AQIDBA=="`.
   - `json.Marshal([4]byte{1, 2, 3, 4})` serializes to a JSON array of numbers `[1,2,3,4]`.
2. **Unmarshaling Base64 into `[N]byte`**:
   - `json.Unmarshal([]byte("\"AQIDBA==\""), &slice)` succeeds and populates `slice` with `[]byte{1, 2, 3, 4}`.
   - `json.Unmarshal([]byte("\"AQIDBA==\""), &arr)` fails with `*json.UnmarshalTypeError` (`json: cannot unmarshal string into Go value of type [4]uint8`).
3. **Unmarshaling Number Arrays into `[N]byte` and `[]byte`**:
   - `json.Unmarshal([]byte("[1,2,3,4]"), &arr)` succeeds and populates `arr` with `[4]byte{1, 2, 3, 4}`.
   - `json.Unmarshal([]byte("[1,2,3,4]"), &slice)` succeeds via general slice unmarshaling.
4. **Signed Byte Slices (`[]int8`)**:
   - Base64 encoding is applied strictly to `[]uint8` (`[]byte`). Signed byte slices `[]int8` serialize as numeric arrays `[1,2,3,4]`.
bytes_test.go
package encoding_json_bytes_test

import (
	"encoding/json"
	"errors"
	"reflect"
	"testing"
)

type HashPayload struct {
	ArrayKey [4]byte `json:"array_key"`
	SliceKey []byte  `json:"slice_key"`
}

// TestByteArray_MarshalsAsNumberArray_NotBase64 proves that fixed-size byte arrays [N]byte
// serialize to a JSON array of numeric bytes [1,2,3,4], NOT an RFC 4648 Base64 string.
func TestByteArray_MarshalsAsNumberArray_NotBase64(t *testing.T) {
	arr := [4]byte{1, 2, 3, 4}

	data, err := json.Marshal(arr)
	if err != nil {
		t.Fatalf("Marshal failed: %v", err)
	}

	// Naive expectation: byte arrays marshal to base64 `"AQIDBA=="` just like []byte.
	// Actual Go encoding/json behavior: [4]byte marshals to JSON array of numbers `[1,2,3,4]`.
	expected := `[1,2,3,4]`
	if string(data) != expected {
		t.Fatalf("expected %s, got %s", expected, string(data))
	}

	// Within a struct
	payload := HashPayload{
		ArrayKey: [4]byte{1, 2, 3, 4},
		SliceKey: []byte{1, 2, 3, 4},
	}
	payloadData, err := json.Marshal(payload)
	if err != nil {
		t.Fatalf("Marshal payload failed: %v", err)
	}

	expectedPayload := `{"array_key":[1,2,3,4],"slice_key":"AQIDBA=="}`
	if string(payloadData) != expectedPayload {
		t.Fatalf("expected %s, got %s", expectedPayload, string(payloadData))
	}
}

// TestByteArray_UnmarshalBase64_Fails proves that json.Unmarshal rejects Base64 strings
// when targeting a fixed-size byte array [N]byte, returning *json.UnmarshalTypeError.
func TestByteArray_UnmarshalBase64_Fails(t *testing.T) {
	var arr [4]byte
	base64JSON := []byte(`"AQIDBA=="`)

	err := json.Unmarshal(base64JSON, &arr)
	if err == nil {
		t.Fatal("expected json.Unmarshal to fail when decoding base64 string into [4]byte, but got nil error")
	}

	var typeErr *json.UnmarshalTypeError
	if !errors.As(err, &typeErr) {
		t.Fatalf("expected *json.UnmarshalTypeError, got %T: %v", err, err)
	}

	if typeErr.Value != "string" {
		t.Fatalf("expected typeErr.Value to be 'string', got %s", typeErr.Value)
	}
}

// TestByteArray_UnmarshalNumberArray_Succeeds proves that json.Unmarshal decodes a JSON numeric array
// directly into a fixed-size byte array [N]byte.
func TestByteArray_UnmarshalNumberArray_Succeeds(t *testing.T) {
	var arr [4]byte
	numberArrayJSON := []byte(`[1,2,3,4]`)

	err := json.Unmarshal(numberArrayJSON, &arr)
	if err != nil {
		t.Fatalf("Unmarshal failed: %v", err)
	}

	expected := [4]byte{1, 2, 3, 4}
	if arr != expected {
		t.Fatalf("expected %v, got %v", expected, arr)
	}
}

// TestByteSlice_MarshalsAsBase64_NotNumberArray proves that byte slices []byte
// serialize to RFC 4648 Base64 string literals, unlike fixed-size byte arrays.
func TestByteSlice_MarshalsAsBase64_NotNumberArray(t *testing.T) {
	slice := []byte{1, 2, 3, 4}

	data, err := json.Marshal(slice)
	if err != nil {
		t.Fatalf("Marshal failed: %v", err)
	}

	expected := `"AQIDBA=="`
	if string(data) != expected {
		t.Fatalf("expected %s, got %s", expected, string(data))
	}

	// Empty slice marshals to empty base64 string ""
	emptySlice := []byte{}
	emptyData, err := json.Marshal(emptySlice)
	if err != nil {
		t.Fatalf("Marshal empty slice failed: %v", err)
	}
	if string(emptyData) != `""` {
		t.Fatalf("expected \"\", got %s", string(emptyData))
	}

	// Nil slice marshals to JSON null
	var nilSlice []byte
	nilData, err := json.Marshal(nilSlice)
	if err != nil {
		t.Fatalf("Marshal nil slice failed: %v", err)
	}
	if string(nilData) != `null` {
		t.Fatalf("expected null, got %s", string(nilData))
	}
}

// TestByteSlice_UnmarshalBase64_Succeeds proves that json.Unmarshal decodes Base64 strings into []byte.
func TestByteSlice_UnmarshalBase64_Succeeds(t *testing.T) {
	var slice []byte
	base64JSON := []byte(`"AQIDBA=="`)

	err := json.Unmarshal(base64JSON, &slice)
	if err != nil {
		t.Fatalf("Unmarshal failed: %v", err)
	}

	expected := []byte{1, 2, 3, 4}
	if !reflect.DeepEqual(slice, expected) {
		t.Fatalf("expected %v, got %v", expected, slice)
	}
}

// TestByteSlice_UnmarshalNumberArray_Succeeds proves that json.Unmarshal can decode a JSON numeric array
// into a byte slice []byte (via generic slice unmarshaling), creating an asymmetry with [N]byte.
func TestByteSlice_UnmarshalNumberArray_Succeeds(t *testing.T) {
	var slice []byte
	numberArrayJSON := []byte(`[1,2,3,4]`)

	err := json.Unmarshal(numberArrayJSON, &slice)
	if err != nil {
		t.Fatalf("Unmarshal failed: %v", err)
	}

	expected := []byte{1, 2, 3, 4}
	if !reflect.DeepEqual(slice, expected) {
		t.Fatalf("expected %v, got %v", expected, slice)
	}
}

// TestSignedByteSlice_MarshalsAsNumberArray proves that base64 encoding is exclusive to []uint8 / []byte,
// and []int8 marshals as a numeric array.
func TestSignedByteSlice_MarshalsAsNumberArray(t *testing.T) {
	signedSlice := []int8{1, 2, 3, 4}

	data, err := json.Marshal(signedSlice)
	if err != nil {
		t.Fatalf("Marshal failed: %v", err)
	}

	expected := `[1,2,3,4]`
	if string(data) != expected {
		t.Fatalf("expected %s, got %s", expected, string(data))
	}
}
csx.json
{"case":{"believed":"json.Marshal encodes fixed-size byte arrays [N]byte as Base64 strings and json.Unmarshal decodes Base64 strings into byte arrays.","caseId":"case:sha256:a0cc52be3051c8469675a120ec308bb66f1a4253c8f8e5352054d6bd633d4d08","contract":["assert json.Marshal encodes fixed-size byte array [N]byte as a JSON numeric array instead of a base64 string","assert json.Unmarshal of a base64 string into [N]byte fails with UnmarshalTypeError","assert json.Unmarshal of a JSON numeric array into [N]byte succeeds","assert json.Marshal encodes byte slice []byte as a base64 string","assert json.Unmarshal of a base64 string into []byte succeeds","assert json.Unmarshal of a JSON numeric array into []byte succeeds","assert json.Marshal encodes signed byte slice []int8 as a numeric array"],"goal":"json.Marshal encodes byte slices as RFC 4648 Base64 strings but encodes fixed-size byte arrays as JSON numeric arrays, and json.Unmarshal rejects Base64 strings for byte arrays","kind":"HOW","packages":["pkg:golang/encoding/json@1.26.5"],"schemaVersion":1,"symbols":["encoding/json.Marshal","encoding/json.Unmarshal"]},"contractCommand":["go","test","./..."],"environment":{"arch":"x64","ecosystem":"golang","executionContext":"go","language":"go","os":"linux","packageManager":"gomod","runtime":"go","schemaVersion":1},"license":"MIT-0","packages":["pkg:golang/encoding/json@1.26.5"],"schemaVersion":1,"symbols":["encoding/json.Marshal","encoding/json.Unmarshal"],"verifierAdapter":"golang@1"}
go.mod
module encoding_json_bytes_sample

go 1.26.5

オリジンシーダー

csx-seed