big.Int.MarshalText()

2025-06-01

big.Int.MarshalText()とは

big.Int.MarshalText()は、Go言語のmath/bigパッケージで提供される多倍長整数型big.Intのメソッドです。これは、encoding.TextMarshalerインターフェースを実装しており、big.Intの値をテキスト形式のバイトスライスとしてエンコード(シリアライズ)するために使用されます。

具体的には、big.Intの値を10進数の文字列として表現し、それを[]byte型で返します。例えば、big.NewInt(123456789)というbig.Intがあれば、MarshalText()[]byte("123456789")を返します。

インターフェースとしての役割

Go言語には、特定の形式にエンコード/デコードするための標準インターフェースがいくつかあります。encoding.TextMarshalerはその一つで、以下のように定義されています。

type TextMarshaler interface {
    MarshalText() (text []byte, err error)
}

big.Intがこのインターフェースを実装しているということは、以下のような場面で特に便利です。

  1. JSONエンコード/デコード
    encoding/jsonパッケージは、データ型がjson.Marshalerまたはencoding.TextMarshalerを実装している場合、それを利用して自動的にJSON形式に変換します。これにより、big.Intを直接JSONに含めることができ、その値は10進数の文字列として表現されます。

    import (
        "encoding/json"
        "fmt"
        "math/big"
    )
    
    type MyData struct {
        Value *big.Int `json:"value"`
    }
    
    func main() {
        num := big.NewInt(1234567890123456789)
        data := MyData{Value: num}
    
        jsonBytes, err := json.Marshal(data)
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(string(jsonBytes)) // {"value":"1234567890123456789"} と出力される
    }
    

使用例

基本的な使用方法は以下の通りです。

package main

import (
    "fmt"
    "math/big"
)

func main() {
    // big.Int のインスタンスを作成
    num := new(big.Int)
    num.SetString("98765432109876543210", 10) // 10進数で大きな数を設定

    // MarshalText() を呼び出してテキスト形式のバイトスライスを取得
    textBytes, err := num.MarshalText()
    if err != nil {
        fmt.Println("エラー:", err)
        return
    }

    // バイトスライスを文字列に変換して表示
    fmt.Println("テキスト形式:", string(textBytes)) // 出力: テキスト形式: 98765432109876543210

    // nil の big.Int を MarshalText() した場合
    var nilNum *big.Int
    nilTextBytes, err := nilNum.MarshalText()
    if err != nil {
        fmt.Println("nilの場合のエラー:", err)
    } else {
        fmt.Println("nilの場合のテキスト形式:", string(nilTextBytes)) // 出力: nilの場合のテキスト形式: 0 (Go 1.15以降の挙動)
    }
}
  • Go 1.15以降では、nilの*big.Intに対してMarshalText()を呼び出すと、"0"を意味するバイトスライスが返されます。それ以前のバージョンでは、空のバイトスライスが返されることがありました。
  • エラーは、基本的にメモリ不足などのシステムレベルの問題がない限り発生しません。big.Intの値が不正であるといった理由でエラーが返されることは通常ありません。
  • MarshalText()big.Intの値を10進数の文字列としてエンコードします。もし異なる基数(例:16進数)で表現したい場合は、big.Int.Text()big.Int.String()メソッドを使用し、適切な基数を指定する必要があります。


big.Int.MarshalText()メソッド自体は非常にシンプルで、内部的にエラーを返すケースはごく稀です。しかし、その利用方法や連携するシステムとの間で問題が発生することがあります。

MarshalText()自体がエラーを返すケース (非常に稀)

  • トラブルシューティング
    • システムのリソース(メモリ)が不足していないか確認してください。
    • 非常に巨大なbig.Intの値を扱う場合、その文字列表現も非常に長大になり、それなりのメモリを消費します。もしOOMが疑われるなら、より効率的なストレージ方法を検討するか、処理を分割することを検討してください。
  • エラーの状況
    MarshalText()メソッドが実際にエラーを返すのは、メモリ割り当ての失敗(OOM: Out Of Memory)など、システムレベルの深刻な問題が発生した場合に限られます。これはGoランタイムやOSの制約に起因するもので、アプリケーションのロジックエラーではありません。

nilの*big.IntをMarshalText()した場合の挙動の誤解

  • トラブルシューティング
    • Goのバージョンを確認し、そのバージョンの挙動を理解してください。
    • もし、nilbig.Intが空文字列としてシリアライズされることを期待するなら、MarshalText()を呼び出す前にnilチェックを行い、手動で空のバイトスライスを返すロジックを実装してください。
      type MyData struct {
          Value *big.Int `json:"value"`
      }
      
      // カスタムのJSONマーシャリングを実装する例
      func (md MyData) MarshalJSON() ([]byte, error) {
          if md.Value == nil {
              return []byte(`{"value":""}`), nil // もしくは `{"value":null}`
          }
          // 通常のMarshalText()に任せる
          return json.Marshal(struct {
              Value *big.Int `json:"value"`
          }{
              Value: md.Value,
          })
      }
      
  • エラーの状況
    Go 1.15より前のバージョンでは、nil*big.Intに対してMarshalText()を呼び出すと、空のバイトスライス([]byte{})が返されました。しかし、Go 1.15以降では、"0"を表すバイトスライス([]byte("0"))が返されるようになりました。この変更を知らないと、期待する出力と異なる場合があります。
    // Go 1.14 以前
    var nilInt *big.Int
    text, _ := nilInt.MarshalText()
    fmt.Println(string(text)) // "" (空文字列)
    
    // Go 1.15 以降
    var nilInt *big.Int
    text, _ := nilInt.MarshalText()
    fmt.Println(string(text)) // "0"
    

JSONエンコード/デコード時の問題

MarshalText()が最も一般的に使われるのは、encoding/jsonパッケージによるJSONエンコード時です。

  • トラブルシューティング

    • 「数値として扱われることを期待したが文字列になった」
      これはbig.Intencoding.TextMarshalerを実装している仕様通りの挙動です。JSONの仕様上、大きな整数は通常文字列として扱うのがベストプラクティスです。もし数値として扱いたい場合は、カスタムマーシャラーを実装し、num.String()ではなく、直接fmt.Sprintf("%s", num)などでJSONに埋め込む必要がありますが、これは推奨されません。JSONデコーダによっては、非常に大きな数値を正しくパースできない可能性があります。
    • 「デコード時にパースできない」
      JSON文字列としてエンコードされたbig.Intをデコードするには、big.Intencoding.TextUnmarshalerを実装しているため、通常は問題ありません。しかし、JSON文字列が数値として扱われている場合、json.Unmarshal()はエラーを返す可能性があります。
      // JSONが "value": 12345678901234567890 のように数値として来ている場合
      // big.Int はこれを文字列としてしか期待しないため、エラーになる可能性がある
      // または、その数値がGoのint64の範囲内に収まらない場合、json.Unmarshalがエラーを返す
      
      • 解決策
        JSONを生成する側がbig.Intを常に文字列として出力するように徹底してください(MarshalText()の挙動がそれです)。デコードする側も文字列として受け取るようにしてください。
    • 「クライアント側で丸められる」
      これはGo側の問題ではなく、クライアント側の言語(JavaScriptなど)の数値型の制約によるものです。
      • 解決策
        JSONを生成する際にbig.Intを文字列として出力する (MarshalText()のデフォルト挙動)。クライアント側でその文字列を受け取り、適切な多倍長整数ライブラリ(例: JavaScriptのBigInt)を使用してパースしてください。これは、big.Intを使用する最大の理由の一つでもあります。
    • big.IntがJSON内で文字列としてではなく数値として扱われることを期待したが、実際には文字列としてエンコードされた。
    • JSONからデコードする際に、big.Intの値が正しくパースできない。
    • JavaScriptなどのクライアント側で、非常に大きな整数が丸められてしまう(JavaScriptのNumber型は64ビット浮動小数点数で、正確に表現できる整数には限界があるため)。

異なる基数での表現

  • トラブルシューティング
    • big.Int.Text(base int)メソッドを使用してください。これにより、任意の基数で文字列表現を得ることができます。
      num := big.NewInt(255)
      hexText := num.Text(16) // "ff"
      fmt.Println(hexText)
      
      // もしJSONで特定の基数で出力したいなら、カスタムマーシャラーを実装する
      type MyHexData struct {
          Value *big.Int `json:"value"`
      }
      
      func (mhd MyHexData) MarshalJSON() ([]byte, error) {
          if mhd.Value == nil {
              return []byte("null"), nil
          }
          hexStr := fmt.Sprintf(`"%s"`, mhd.Value.Text(16)) // 16進数文字列をJSON文字列として囲む
          return []byte(hexStr), nil
      }
      
      data := MyHexData{Value: big.NewInt(255)}
      jsonBytes, _ := json.Marshal(data)
      fmt.Println(string(jsonBytes)) // "ff" とはならず、`{"value":"ff"}` のように出力される
      
  • エラーの状況
    MarshalText()は常に10進数でエンコードします。もし16進数や別の基数でbig.Intを表現したい場合、MarshalText()は適切なメソッドではありません。


big.Int.MarshalText()は主にencoding.TextMarshalerインターフェースの実装として機能し、特にencoding/jsonパッケージと組み合わせて使われることが多いです。ここでは、基本的な使い方から、JSONとの連携、nil値の扱いまで、具体的なコード例を挙げて説明します。

基本的なMarshalText()の使用

MarshalText()は、big.Intの値を10進数のバイトスライスとして返します。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// 大きな整数を big.Int で定義
	numStr := "1234567890123456789012345678901234567890"
	num := new(big.Int)
	num.SetString(numStr, 10) // 10進数で文字列をセット

	fmt.Printf("元の big.Int の値: %s\n", num.String())

	// MarshalText() を呼び出してバイトスライスを取得
	textBytes, err := num.MarshalText()
	if err != nil {
		fmt.Printf("MarshalText() エラー: %v\n", err)
		return
	}

	// バイトスライスを文字列に変換して表示
	fmt.Printf("MarshalText() の結果 (string): %s\n", string(textBytes))
	fmt.Printf("MarshalText() の結果 ([]byte): %v\n", textBytes)

	// 非常に小さな値の場合
	smallNum := big.NewInt(123)
	smallTextBytes, err := smallNum.MarshalText()
	if err != nil {
		fmt.Printf("MarshalText() (小) エラー: %v\n", err)
		return
	}
	fmt.Printf("MarshalText() (小) の結果: %s\n", string(smallTextBytes))
}

出力例

元の big.Int の値: 1234567890123456789012345678901234567890
MarshalText() の結果 (string): 1234567890123456789012345678901234567890
MarshalText() の結果 ([]byte): [49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57 48 49 50 51 52 53 54 55 56 57 48]
MarshalText() (小) の結果: 123

この例では、big.Intの値がそのまま10進数の文字列としてバイトスライスに変換されることがわかります。

JSONエンコード/デコードでの使用

MarshalText()の最も一般的なユースケースです。big.Intencoding.TextMarshalerを実装しているため、json.Marshalは自動的にMarshalText()を呼び出し、big.IntをJSON文字列としてエンコードします。

package main

import (
	"encoding/json"
	"fmt"
	"math/big"
)

// JSONにエンコード/デコードしたい構造体
type Data struct {
	ID    int      `json:"id"`
	Value *big.Int `json:"value"` // big.Int はポインタ型で宣言するのが一般的
	Price *big.Int `json:"price,omitempty"` // nilの場合はJSONに出力しない
}

func main() {
	// 構造体のインスタンスを作成
	num1 := new(big.Int)
	num1.SetString("987654321098765432109876543210", 10)

	data1 := Data{
		ID:    1,
		Value: num1,
		Price: big.NewInt(500), // 値がある場合
	}

	// 構造体をJSONにエンコード
	jsonData1, err := json.MarshalIndent(data1, "", "  ") // 読みやすくするためにインデント付きで
	if err != nil {
		fmt.Printf("JSON Marshal エラー: %v\n", err)
		return
	}
	fmt.Println("--- JSON エンコード例 ---")
	fmt.Println(string(jsonData1))

	// nilのbig.Intを含む構造体
	data2 := Data{
		ID:    2,
		Value: big.NewInt(100), // 値がある場合
		Price: nil,             // nil の場合
	}
	jsonData2, err := json.MarshalIndent(data2, "", "  ")
	if err != nil {
		fmt.Printf("JSON Marshal エラー: %v\n", err)
		return
	}
	fmt.Println("\n--- nil の big.Int (Price) を含む JSON エンコード例 ---")
	fmt.Println(string(jsonData2))
	// 注意: Go 1.15以降では Value: nil の場合 "value":"0" となるが、omitemptyがあれば Price: nil は出力されない

	// JSON文字列から構造体にデコード
	fmt.Println("\n--- JSON デコード例 ---")
	jsonStr := `{
		"id": 3,
		"value": "1122334455667788990011223344556677889900",
		"price": "1000"
	}`

	var decodedData Data
	err = json.Unmarshal([]byte(jsonStr), &decodedData)
	if err != nil {
		fmt.Printf("JSON Unmarshal エラー: %v\n", err)
		return
	}

	fmt.Printf("デコードされた ID: %d\n", decodedData.ID)
	fmt.Printf("デコードされた Value: %s (型: %T)\n", decodedData.Value.String(), decodedData.Value)
	if decodedData.Price != nil {
		fmt.Printf("デコードされた Price: %s (型: %T)\n", decodedData.Price.String(), decodedData.Price)
	} else {
		fmt.Println("デコードされた Price: nil")
	}

	// 値が0のケース (Go 1.15以降でMarshalText()が"0"を返す挙動)
	fmt.Println("\n--- big.Int が 0 の場合の JSON エンコード ---")
	dataZero := Data{ID: 4, Value: big.NewInt(0)}
	jsonZero, err := json.MarshalIndent(dataZero, "", "  ")
	if err != nil {
		fmt.Printf("JSON Marshal (Zero) エラー: %v\n", err)
		return
	}
	fmt.Println(string(jsonZero)) // {"id": 4, "value": "0", "price": null} となる
}

出力例

--- JSON エンコード例 ---
{
  "id": 1,
  "value": "987654321098765432109876543210",
  "price": "500"
}

--- nil の big.Int (Price) を含む JSON エンコード例 ---
{
  "id": 2,
  "value": "100"
}

--- JSON デコード例 ---
デコードされた ID: 3
デコードされた Value: 1122334455667788990011223344556677889900 (型: *math.Int)
デコードされた Price: 1000 (型: *math.Int)

--- big.Int が 0 の場合の JSON エンコード ---
{
  "id": 4,
  "value": "0"
}

この例から、big.IntがJSONの文字列として自動的にシリアライズ・デシリアライズされることがわかります。これはMarshalText()UnmarshalText()big.Intが実装)が提供する機能のおかげです。

nilの*big.IntとMarshalText()の挙動(Go 1.15以降)

Go 1.15以降、nil*big.Intに対してMarshalText()を呼び出すと"0"を表すバイトスライスが返されます。この挙動は特にJSONのエンコードで影響します。

package main

import (
	"encoding/json"
	"fmt"
	"math/big"
)

type NilTest struct {
	MaybeValue *big.Int `json:"maybeValue"`
}

func main() {
	fmt.Println("--- nil の *big.Int の MarshalText() 挙動 (Go 1.15以降) ---")

	var nilBigInt *big.Int
	text, err := nilBigInt.MarshalText()
	if err != nil {
		fmt.Printf("MarshalText() エラー: %v\n", err)
	}
	fmt.Printf("nilBigInt.MarshalText() の結果: %s\n", string(text)) // "0" が出力される

	// JSONエンコードの場合
	data := NilTest{MaybeValue: nil}
	jsonData, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		fmt.Printf("JSON Marshal エラー: %v\n", err)
		return
	}
	fmt.Println("\n--- nil の *big.Int を含む JSON エンコード ---")
	fmt.Println(string(jsonData)) // {"maybeValue": "0"} となる
}

出力例

--- nil の *big.Int の MarshalText() 挙動 (Go 1.15以降) ---
nilBigInt.MarshalText() の結果: 0

--- nil の *big.Int を含む JSON エンコード ---
{
  "maybeValue": "0"
}

もしnilの場合にJSONでnullや空文字列を出力したい場合は、json:"omitempty"タグを使うか、カスタムのMarshalJSONメソッドを実装する必要があります。

カスタムのJSONマーシャリングでbig.Intのnilを制御する例

nilbig.IntがJSONで"0"として出力されるのを避けたい場合、カスタムのMarshalJSONメソッドを実装できます。

package main

import (
	"encoding/json"
	"fmt"
	"math/big"
)

type CustomData struct {
	ID    int      `json:"id"`
	Value *big.Int `json:"value"`
}

// CustomData の MarshalJSON メソッドを実装
func (cd CustomData) MarshalJSON() ([]byte, error) {
	// 一時的な構造体を使って、big.Int の部分だけをカスタマイズ
	type Alias CustomData // 無限ループを防ぐためのエイリアス

	if cd.Value == nil {
		// Value が nil の場合、null を出力する
		// または空文字列 "" を出力したい場合は `fmt.Sprintf(`{"id":%d,"value":""}`, cd.ID)` など
		return json.Marshal(&struct {
			Alias
			Value *string `json:"value"` // *string 型で null を表現
		}{
			Alias: Alias(cd),
			Value: nil, // null を出力
		})
	}

	// Value が nil でない場合は、デフォルトのマーシャリング挙動に任せる
	// big.Int は MarshalText() を通じて文字列になる
	return json.Marshal(Alias(cd))
}

func main() {
	fmt.Println("--- カスタム MarshalJSON で nil の big.Int を制御 ---")

	dataWithNil := CustomData{ID: 101, Value: nil}
	jsonWithNil, err := json.MarshalIndent(dataWithNil, "", "  ")
	if err != nil {
		fmt.Printf("JSON Marshal エラー: %v\n", err)
		return
	}
	fmt.Println("Value が nil の場合:")
	fmt.Println(string(jsonWithNil)) // {"id": 101, "value": null} となる

	dataWithValue := CustomData{ID: 102, Value: big.NewInt(998877665544332211)}
	jsonWithValue, err := json.MarshalIndent(dataWithValue, "", "  ")
	if err != nil {
		fmt.Printf("JSON Marshal エラー: %v\n", err)
		return
	}
	fmt.Println("\nValue が値を持つ場合:")
	fmt.Println(string(jsonWithValue)) // {"id": 102, "value": "998877665544332211"} となる
}
--- カスタム MarshalJSON で nil の big.Int を制御 ---
Value が nil の場合:
{
  "id": 101,
  "value": null
}

Value が値を持つ場合:
{
  "id": 102,
  "value": "998877665544332211"
}


MarshalText()encoding.TextMarshalerインターフェースの一部として、主にJSONなどのテキストベースのシリアライゼーションで自動的に呼び出されます。しかし、手動で文字列に変換したり、特定の形式で出力したりする場合には、別のメソッドを使います。

big.Int.String()

これは最も一般的でシンプルな代替方法です。big.Intの値を10進数の文字列として返します。MarshalText()[]byteを返すのに対し、String()stringを返します。

特徴

  • fmt.Print()fmt.Sprintf()などで%sフォーマット動詞を使うと、内部的にこのString()メソッドが呼び出されます。
  • nil*big.Intに対して呼び出すと、Go 1.15以降では "0" を返します。(MarshalText()と同じ挙動)
  • 最も直接的で、デバッグ出力などによく使われる。
  • 常に10進数表現。

コード例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	num := new(big.Int)
	num.SetString("12345678901234567890", 10)

	// String() メソッドの使用
	s := num.String()
	fmt.Printf("String() の結果: %s (型: %T)\n", s, s)

	// fmt.Sprintf() での利用 (内部で String() が呼ばれる)
	formattedStr := fmt.Sprintf("フォーマットされた値: %s", num)
	fmt.Println(formattedStr)

	// nil の big.Int
	var nilNum *big.Int
	fmt.Printf("nil の String() 結果: %s\n", nilNum.String()) // "0" が出力される
}

出力例

String() の結果: 12345678901234567890 (型: string)
フォーマットされた値: 12345678901234567890
nil の String() 結果: 0

big.Int.Text(base int)

これは、指定された基数(base)でbig.Intの値を文字列として表現したい場合に非常に便利です。例えば、16進数や2進数で出力したい場合に使います。

特徴

  • MarshalText()[]byteを返すのに対し、Text()stringを返す。
  • 結果は文字列として返される。
  • base引数で基数を指定できる(2〜62)。

コード例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	num := big.NewInt(255) // 10進数の255

	// 10進数で表示 (String() と同じ)
	fmt.Printf("10進数: %s\n", num.Text(10))

	// 16進数で表示
	fmt.Printf("16進数: %s\n", num.Text(16)) // "ff"

	// 2進数で表示
	fmt.Printf("2進数: %s\n", num.Text(2)) // "11111111"

	// 36進数で表示 (0-9a-z)
	bigNum := new(big.Int)
	bigNum.SetString("123456789012345678901234567890", 10)
	fmt.Printf("36進数: %s\n", bigNum.Text(36))
}

出力例

10進数: 255
16進数: ff
2進数: 11111111
36進数: 6e9m7f8h9m7w4k9c2t7h0

big.Int.Append(buf []byte, base int)

既存のバイトスライスにbig.Intの文字列表現を追記したい場合に効率的です。特に、大きな数を繰り返しエンコードする場合や、メモリ割り当てを最小限に抑えたい場合に有効です。

特徴

  • メモリの再利用が可能になるため、パフォーマンスが向上する可能性がある。
  • 既存の[]byteに追記し、新しい(または拡張された)[]byteを返す。
  • base引数で基数を指定できる。

コード例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	num := big.NewInt(123456)

	// 空のバイトスライスに追記
	buf := make([]byte, 0, 32) // 容量を事前に確保
	buf = num.Append(buf, 10)
	fmt.Printf("Append(10進数): %s (長さ: %d, 容量: %d)\n", string(buf), len(buf), cap(buf))

	// 別のバイトスライスに追記
	anotherBuf := []byte("Prefix: ")
	anotherBuf = num.Append(anotherBuf, 16) // 16進数で追記
	fmt.Printf("Append(16進数、プレフィックスあり): %s\n", string(anotherBuf))
}

出力例

Append(10進数): 123456 (長さ: 6, 容量: 32)
Append(16進数、プレフィックスあり): Prefix: 1e240

カスタムのMarshalJSON() / UnmarshalJSON() メソッド

MarshalText()encoding.TextMarshalerを介してJSONと連携しますが、JSON出力の形式をより細かく制御したい場合(例:nil値をnullとして出力したい、特定のフィールドを数値として扱いたいなど)は、json.Marshalerおよびjson.Unmarshalerインターフェースを直接実装します。

特徴

  • jsonパッケージによる自動変換をバイパスする。
  • big.IntのデフォルトのMarshalText()/UnmarshalText()の挙動を上書きできる。
  • JSONへの変換ロジックを完全にカスタマイズできる。

コード例

package main

import (
	"encoding/json"
	"fmt"
	"math/big"
)

type MyCustomInt struct {
	Value *big.Int `json:"value"`
}

// MarshalJSON は MyCustomInt のJSONエンコードをカスタマイズします。
func (mci MyCustomInt) MarshalJSON() ([]byte, error) {
	if mci.Value == nil {
		return []byte("null"), nil // nil の場合は JSON null を出力
	}
	// big.Int を文字列として出力する (MarshalText() と同じ結果をJSON文字列として返す)
	return []byte(fmt.Sprintf(`"%s"`, mci.Value.String())), nil
}

// UnmarshalJSON は MyCustomInt のJSONデコードをカスタマイズします。
func (mci *MyCustomInt) UnmarshalJSON(data []byte) error {
	s := string(data)
	if s == "null" {
		mci.Value = nil
		return nil
	}

	// JSON文字列から big.Int をパース
	// JSONの "..." 部分を削除する必要がある場合がある
	// 例: `"123"` の " を削除
	if len(s) > 1 && s[0] == '"' && s[len(s)-1] == '"' {
		s = s[1 : len(s)-1]
	}

	num := new(big.Int)
	_, ok := num.SetString(s, 10)
	if !ok {
		return fmt.Errorf("無効な big.Int 文字列: %s", s)
	}
	mci.Value = num
	return nil
}

func main() {
	fmt.Println("--- カスタム MarshalJSON/UnmarshalJSON の例 ---")

	// nil の値のエンコード
	data1 := MyCustomInt{Value: nil}
	json1, _ := json.MarshalIndent(data1, "", "  ")
	fmt.Printf("nil の MarshalJSON: %s\n", string(json1))

	// 値がある場合のエンコード
	data2 := MyCustomInt{Value: big.NewInt(1234567890)}
	json2, _ := json.MarshalIndent(data2, "", "  ")
	fmt.Printf("値がある場合の MarshalJSON: %s\n", string(json2))

	// nil 値のデコード
	jsonStr1 := `{"id":1,"value":null}`
	var decoded1 MyCustomInt
	err := json.Unmarshal([]byte(jsonStr1), &decoded1)
	if err != nil {
		fmt.Printf("Unmarshal エラー (null): %v\n", err)
	} else {
		fmt.Printf("Unmarshal (null) 結果: %v\n", decoded1.Value)
	}

	// 値がある場合のデコード
	jsonStr2 := `{"id":2,"value":"987654321"}`
	var decoded2 MyCustomInt
	err = json.Unmarshal([]byte(jsonStr2), &decoded2)
	if err != nil {
		fmt.Printf("Unmarshal エラー (値あり): %v\n", err)
	} else {
		fmt.Printf("Unmarshal (値あり) 結果: %s\n", decoded2.Value.String())
	}
}

出力例

--- カスタム MarshalJSON/UnmarshalJSON の例 ---
nil の MarshalJSON: {
  "value": null
}
値がある場合の MarshalJSON: {
  "value": "1234567890"
}
Unmarshal (null) 結果: <nil>
Unmarshal (値あり) 結果: 987654321

big.Int.MarshalText()big.Intを10進数のテキスト形式のバイトスライスに変換する標準的な方法であり、特にencoding/jsonパッケージと組み合わせる場合に自動的に機能します。しかし、より柔軟な出力形式(異なる基数)、既存のバッファへの追記、またはJSON出力の特定の挙動(nil値の扱いなど)を詳細に制御したい場合は、String()Text()Append()、またはカスタムのMarshalJSON() / UnmarshalJSON()メソッドが代替手段として有効です。