[インデックス 14970] ファイルの概要
このコミットは、Go言語の encoding/xml
パッケージにおいて、XMLマーシャリング時に ,chardata
タグが付けられたフィールドが []byte
型でない場合でも正しく処理されるようにするものです。具体的には、数値型(int
, uint
, float
)、bool
、string
、そして time.Time
型のフィールドが chardata
としてXMLに出力される際のサポートを追加しています。これにより、Goの構造体からXMLへの変換の柔軟性が向上し、より多様なデータ型をXMLのテキストコンテンツとして扱うことが可能になりました。
コミット
commit 14bd52db3fb56670270aafc79302316afe1ed07c
Author: Vega Garcia Luis Alfonso <vegacom@gmail.com>
Date: Tue Jan 22 22:13:40 2013 -0500
xml: Support fields not of type []byte when marshaling ",chardata"
Fixes #4506.
R=rsc, remyoudompheng
CC=golang-dev
https://golang.org/cl/7106045
---
src/pkg/encoding/xml/marshal.go | 13 +++++++++++++
src/pkg/encoding/xml/marshal_test.go | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+)
diff --git a/src/pkg/encoding/xml/marshal.go b/src/pkg/encoding/xml/marshal.go
index 8b2f4173f3..383fb26b04 100644
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -279,13 +279,26 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
t\tvf := finfo.value(val)
t\tswitch finfo.flags & fMode {
t\tcase fCharData:
+\t\t\tvar scratch [64]byte
\t\t\tswitch vf.Kind() {\
+\t\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+\t\t\t\tEscape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10))\
+\t\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+\t\t\t\tEscape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10))\
+\t\t\tcase reflect.Float32, reflect.Float64:
+\t\t\t\tEscape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits()))\
+\t\t\tcase reflect.Bool:
+\t\t\t\tEscape(p, strconv.AppendBool(scratch[:0], vf.Bool()))
\t\t\tcase reflect.String:
\t\t\t\tEscape(p, []byte(vf.String()))
\t\t\tcase reflect.Slice:
\t\t\t\tif elem, ok := vf.Interface().([]byte); ok {
\t\t\t\t\tEscape(p, elem)
\t\t\t\t}\
+\t\t\tcase reflect.Struct:
+\t\t\t\tif vf.Type() == timeType {
+\t\t\t\t\tEscape(p, []byte(vf.Interface().(time.Time).Format(time.RFC3339Nano)))\
+\t\t\t\t}\
\t\t\t}\
\t\t\tcontinue
\n
diff --git a/src/pkg/encoding/xml/marshal_test.go b/src/pkg/encoding/xml/marshal_test.go
index 2ce7721abd..67fcfd9ed5 100644
--- a/src/pkg/encoding/xml/marshal_test.go
+++ b/src/pkg/encoding/xml/marshal_test.go
@@ -59,6 +59,36 @@ type Book struct {
Title string `xml:",chardata"`
}
+type Event struct {
+\tXMLName struct{} `xml:"event"`
+\tYear int `xml:",chardata"`
+}
+
+type Movie struct {
+\tXMLName struct{} `xml:"movie"`
+\tLength uint `xml:",chardata"`
+}
+
+type Pi struct {
+\tXMLName struct{} `xml:"pi"`
+\tApproximation float32 `xml:",chardata"`
+}
+
+type Universe struct {
+\tXMLName struct{} `xml:"universe"`
+\tVisible float64 `xml:",chardata"`
+}
+
+type Particle struct {
+\tXMLName struct{} `xml:"particle"`
+\tHasMass bool `xml:",chardata"`
+}
+
+type Departure struct {
+\tXMLName struct{} `xml:"departure"`
+\tWhen time.Time `xml:",chardata"`
+}
+
type SecretAgent struct {
\tXMLName struct{} `xml:"agent"`
\tHandle string `xml:"handle,attr"`
@@ -345,6 +375,12 @@ var marshalTests = []struct {
{Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`},
{Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
{Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`},
+\t{Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
+\t{Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
+\t{Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
+\t{Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
+\t{Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
+\t{Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
{Value: atomValue, ExpectXML: atomXml},
{
\tValue: &Ship{
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/14bd52db3fb56670270aafc79302316afe1ed07c
元コミット内容
xml: Support fields not of type []byte when marshaling ",chardata"
Fixes #4506.
R=rsc, remyoudompheng
CC=golang-dev
https://golang.org/cl/7106045
変更の背景
Go言語の encoding/xml
パッケージは、Goの構造体とXMLの間でデータを変換(マーシャリングとアンマーシャリング)するための標準ライブラリです。XML要素のテキストコンテンツ(子要素や属性ではない部分)は「文字データ(character data)」と呼ばれ、Goの構造体では通常、xml:",chardata"
タグを使用して対応するフィールドにマッピングされます。
このコミットが導入される前は、xml:",chardata"
タグが付けられた構造体フィールドは、XMLにマーシャリングされる際に []byte
型である必要がありました。しかし、Goのプログラムでは、XMLの文字データとして数値(整数、浮動小数点数)、真偽値、文字列、日付/時刻など、さまざまな型のデータを扱うことが一般的です。これらのデータをXMLにマーシャリングする際に、いちいち []byte
型に変換する手間が発生したり、あるいは []byte
以外の型を chardata
として扱えないという制限がありました。
この制限は、Goの encoding/xml
パッケージの柔軟性を損ない、開発者がXMLを扱う際の不便さや冗長なコードの原因となっていました。コミットメッセージにある "Fixes #4506" は、この問題がGoのIssueトラッカーで報告され、修正が求められていたことを示唆しています。このコミットは、この既存の制約を取り除き、より自然なGoのデータ型をXMLの文字データとして直接マーシャリングできるようにすることで、開発体験を向上させることを目的としています。
前提知識の解説
Go言語の encoding/xml
パッケージ
encoding/xml
パッケージは、Goの構造体とXMLドキュメントの間でデータをエンコード(マーシャリング)およびデコード(アンマーシャリング)するための機能を提供します。
- マーシャリング (Marshaling): Goの構造体のデータをXML形式のバイト列に変換するプロセスです。
xml.Marshal
関数がこれを行います。 - アンマーシャリング (Unmarshaling): XML形式のバイト列をGoの構造体のデータに変換するプロセスです。
xml.Unmarshal
関数がこれを行います。
xml:",chardata"
タグ
Goの構造体フィールドに xml:",chardata"
タグを付けると、そのフィールドがXML要素のテキストコンテンツ(文字データ)として扱われることを encoding/xml
パッケージに指示します。
例:
type Book struct {
XMLName xml.Name `xml:"book"`
Title string `xml:",chardata"` // このフィールドが <book>要素のテキストコンテンツになる
}
// Book{Title: "Go Programming"} をマーシャリングすると
// <book>Go Programming</book> のようになる
reflect
パッケージ
Goの reflect
パッケージは、実行時にプログラムの構造(型、フィールド、メソッドなど)を検査および操作するための機能を提供します。encoding/xml
のような汎用的なエンコーディング/デコーディングライブラリでは、構造体のフィールドの型を動的に判断し、それに応じて異なる処理を行うために reflect
パッケージが広く利用されます。
reflect.Value
: 任意のGoの値の実行時データ表現です。reflect.Kind()
:reflect.Value
が表す値の具体的な種類(例:reflect.Int
,reflect.String
,reflect.Slice
,reflect.Struct
など)を返します。reflect.Int()
,reflect.Uint()
,reflect.Float()
,reflect.Bool()
,reflect.String()
: それぞれのreflect.Value
を対応するGoの基本型に変換します。reflect.Type()
:reflect.Value
が表す値の型情報を返します。vf.Interface().([]byte)
:reflect.Value
を元のインターフェース型に戻し、型アサーション.([]byte)
を使って[]byte
型に変換を試みます。
strconv
パッケージ
strconv
パッケージは、基本データ型(数値、真偽値など)と文字列の間で変換を行うための機能を提供します。このコミットでは、数値や真偽値をXMLの文字データとして出力するために、これらの値を文字列に変換する際に strconv
パッケージの関数(例: AppendInt
, AppendUint
, AppendFloat
, AppendBool
)が使用されています。これらの Append
系関数は、既存のバイトスライスに変換結果を追記する形式で、効率的な文字列変換を可能にします。
time
パッケージ
Goの time
パッケージは、時刻の表現と操作のための機能を提供します。time.Time
型は特定の日時を表し、Format
メソッドを使用して指定されたレイアウトで文字列にフォーマットできます。XMLでは、日付/時刻は通常ISO 8601形式(例: 2013-01-09T00:15:00-09:00
)で表現されます。
技術的詳細
このコミットの核心は、src/pkg/encoding/xml/marshal.go
内の printer
型の marshalStruct
メソッドの変更にあります。このメソッドは、Goの構造体をXMLにマーシャリングする際の主要なロジックを含んでいます。
変更前は、fCharData
フラグ(xml:",chardata"
タグに対応)が設定されたフィールドに対しては、その値が reflect.String
型か []byte
型であることのみを想定していました。[]byte
型の場合は直接 Escape
関数でXMLエスケープして出力し、string
型の場合は []byte
に変換してから Escape
していました。
このコミットでは、fCharData
の case
ブロック内に新しい switch vf.Kind()
ステートメントが追加され、chardata
フィールドが取りうる様々なGoの基本データ型を明示的に処理するように拡張されました。
-
reflect.Int
,reflect.Int8
,reflect.Int16
,reflect.Int32
,reflect.Int64
:vf.Int()
でint64
型の値を取得します。strconv.AppendInt(scratch[:0], vf.Int(), 10)
を使用して、この整数値を10進数文字列としてscratch
バイトスライスに追記します。scratch[:0]
は、scratch
の容量を再利用しつつ、長さを0にリセットするイディオムです。- 結果のバイトスライスを
Escape
関数に渡し、XMLエスケープして出力します。
-
reflect.Uint
,reflect.Uint8
,reflect.Uint16
,reflect.Uint32
,reflect.Uint64
,reflect.Uintptr
:vf.Uint()
でuint64
型の値を取得します。strconv.AppendUint(scratch[:0], vf.Uint(), 10)
を使用して、この符号なし整数値を10進数文字列としてscratch
バイトスライスに追記します。- 結果のバイトスライスを
Escape
関数に渡し、XMLエスケープして出力します。
-
reflect.Float32
,reflect.Float64
:vf.Float()
でfloat64
型の値を取得します。strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits())
を使用して、浮動小数点数値を文字列に変換します。'g'
は、指数表記または通常の表記のいずれか短い方を選択するフォーマットです。-1
は、精度を自動的に決定することを意味します。vf.Type().Bits()
は、元の浮動小数点数がfloat32
(32ビット) かfloat64
(64ビット) かに応じて、適切なビット数を指定します。
- 結果のバイトスライスを
Escape
関数に渡し、XMLエスケープして出力します。
-
reflect.Bool
:vf.Bool()
でbool
型の値を取得します。strconv.AppendBool(scratch[:0], vf.Bool())
を使用して、真偽値を "true" または "false" の文字列としてscratch
バイトスライスに追記します。- 結果のバイトスライスを
Escape
関数に渡し、XMLエスケープして出力します。
-
reflect.Struct
(特にtime.Time
型):vf.Type() == timeType
で、フィールドの型がtime.Time
であるかをチェックします。timeType
はreflect.TypeOf((*time.Time)(nil)).Elem()
で事前に取得されたtime.Time
型のreflect.Type
です。vf.Interface().(time.Time)
でreflect.Value
をtime.Time
型に型アサーションします。Format(time.RFC3339Nano)
を使用して、time.Time
の値をRFC3339Nano形式の文字列にフォーマットします。これはXMLで日付/時刻を表現する際の一般的な形式です。- 結果の文字列を
[]byte
に変換し、Escape
関数に渡し、XMLエスケープして出力します。
これらの変更により、encoding/xml
パッケージは、Goの多様な基本データ型を chardata
としてXMLにシームレスにマーシャリングできるようになり、開発者はXML構造とGoのデータモデルをより自然にマッピングできるようになりました。
コアとなるコードの変更箇所
diff --git a/src/pkg/encoding/xml/marshal.go b/src/pkg/encoding/xml/marshal.go
index 8b2f4173f3..383fb26b04 100644
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -279,13 +279,26 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
t\tvf := finfo.value(val)
t\tswitch finfo.flags & fMode {
t\tcase fCharData:
+\t\t\tvar scratch [64]byte
\t\t\tswitch vf.Kind() {\
+\t\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+\t\t\t\tEscape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10))\
+\t\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+\t\t\t\tEscape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10))\
+\t\t\tcase reflect.Float32, reflect.Float64:
+\t\t\t\tEscape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits()))\
+\t\t\tcase reflect.Bool:
+\t\t\t\tEscape(p, strconv.AppendBool(scratch[:0], vf.Bool()))
\t\t\tcase reflect.String:
\t\t\t\tEscape(p, []byte(vf.String()))
\t\t\tcase reflect.Slice:
\t\t\t\tif elem, ok := vf.Interface().([]byte); ok {
\t\t\t\t\tEscape(p, elem)
\t\t\t\t}\
+\t\t\tcase reflect.Struct:
+\t\t\t\tif vf.Type() == timeType {
+\t\t\t\t\tEscape(p, []byte(vf.Interface().(time.Time).Format(time.RFC3339Nano)))\
+\t\t\t\t}\
\t\t\t}\
\t\t\tcontinue
\n
diff --git a/src/pkg/encoding/xml/marshal_test.go b/src/pkg/encoding/xml/marshal_test.go
index 2ce7721abd..67fcfd9ed5 100644
--- a/src/pkg/encoding/xml/marshal_test.go
+++ b/src/pkg/encoding/xml/marshal_test.go
@@ -59,6 +59,36 @@ type Book struct {
Title string `xml:",chardata"`
}
+type Event struct {
+\tXMLName struct{} `xml:"event"`
+\tYear int `xml:",chardata"`
+}
+
+type Movie struct {
+\tXMLName struct{} `xml:"movie"`
+\tLength uint `xml:",chardata"`
+}
+
+type Pi struct {
+\tXMLName struct{} `xml:"pi"`
+\tApproximation float32 `xml:",chardata"`
+}
+
+type Universe struct {
+\tXMLName struct{} `xml:"universe"`
+\tVisible float64 `xml:",chardata"`
+}
+
+type Particle struct {
+\tXMLName struct{} `xml:"particle"`
+\tHasMass bool `xml:",chardata"`
+}
+
+type Departure struct {
+\tXMLName struct{} `xml:"departure"`
+\tWhen time.Time `xml:",chardata"`
+}
+
type SecretAgent struct {
\tXMLName struct{} `xml:"agent"`
\tHandle string `xml:"handle,attr"`
@@ -345,6 +375,12 @@ var marshalTests = []struct {
{Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`},
{Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
{Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`},
+\t{Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
+\t{Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
+\t{Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
+\t{Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
+\t{Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
+\t{Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
{Value: atomValue, ExpectXML: atomXml},
{
\tValue: &Ship{
コアとなるコードの解説
src/pkg/encoding/xml/marshal.go
の変更点
marshal.go
の変更は、printer
型の marshalStruct
メソッド内の fCharData
の処理ロジックを拡張しています。
-
var scratch [64]byte
の追加:- これは、
strconv.Append...
関数が文字列変換結果を書き込むための一時的なバイトスライスです。64バイトは、一般的な数値や真偽値の文字列表現を格納するのに十分なサイズです。これにより、毎回新しいスライスをアロケートするオーバーヘッドを避けることができます。
- これは、
-
switch vf.Kind()
の追加と各型のハンドリング:vf.Kind()
は、現在処理している構造体フィールドの値vf
の具体的な種類(reflect.Int
,reflect.String
など)を返します。case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
:vf.Int()
でフィールドの整数値を取得し、strconv.AppendInt
で10進数文字列に変換してscratch
に書き込みます。Escape(p, ...)
でXMLエスケープ処理を行い、出力ストリームp
に書き出します。
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
:vf.Uint()
でフィールドの符号なし整数値を取得し、strconv.AppendUint
で10進数文字列に変換してscratch
に書き込みます。- 同様に
Escape
して出力します。
case reflect.Float32, reflect.Float64:
:vf.Float()
でフィールドの浮動小数点数値を取得し、strconv.AppendFloat
で文字列に変換します。'g'
フォーマットは、数値の大きさに応じて最適な表現(通常の表記または指数表記)を選択します。-1
は最小限の桁数で表現することを意味し、vf.Type().Bits()
は元の型のビット数(32または64)を渡します。- 同様に
Escape
して出力します。
case reflect.Bool:
:vf.Bool()
でフィールドの真偽値を取得し、strconv.AppendBool
で "true" または "false" の文字列に変換してscratch
に書き込みます。- 同様に
Escape
して出力します。
case reflect.String:
:- 既存の
string
型の処理はそのまま残ります。[]byte(vf.String())
でバイトスライスに変換し、Escape
して出力します。
- 既存の
case reflect.Slice:
:- 既存の
[]byte
型の処理もそのまま残ります。vf.Interface().([]byte)
で[]byte
型への型アサーションを試み、成功すれば直接Escape
して出力します。
- 既存の
case reflect.Struct:
:- 構造体型の場合、特に
time.Time
型を特別扱いします。 if vf.Type() == timeType
で、フィールドの型がtime.Time
であるかをチェックします。vf.Interface().(time.Time)
でtime.Time
型に変換し、Format(time.RFC3339Nano)
でRFC3339Nano形式の文字列にフォーマットします。- 結果の文字列を
[]byte
に変換し、Escape
して出力します。
- 構造体型の場合、特に
これらの変更により、marshalStruct
は chardata
フィールドの型に応じて適切な文字列変換を行い、XMLとして出力できるようになりました。
src/pkg/encoding/xml/marshal_test.go
の変更点
marshal_test.go
の変更は、新しい chardata
のマーシャリング機能が正しく動作することを検証するためのテストケースの追加です。
-
新しい構造体の定義:
Event
(int),Movie
(uint),Pi
(float32),Universe
(float64),Particle
(bool),Departure
(time.Time) といった、様々なデータ型をxml:",chardata"
フィールドとして持つ新しい構造体が定義されています。これらの構造体は、それぞれの型のchardata
マーシャリングをテストするために使用されます。
-
marshalTests
スライスへのテストケースの追加:marshalTests
は、Goの構造体をXMLにマーシャリングした結果が期待通りになるかを検証するためのテストケースの集合です。- 新しく定義された構造体(
Event
,Movie
,Pi
,Universe
,Particle
,Departure
)のインスタンスと、それらをマーシャリングした際に期待されるXML文字列が追加されています。 - 例えば、
{Value: &Event{Year: -3114}, ExpectXML:
-3114 }
は、int
型のYear
フィールドがchardata
として正しくXMLに出力されることをテストします。 time.Time
型のテストケースでは、ParseTime
ヘルパー関数を使用して特定の時刻を生成し、それがRFC3339Nano形式でXMLに出力されることを確認しています。
これらのテストケースの追加により、encoding/xml
パッケージの chardata
マーシャリング機能が、[]byte
以外の様々なデータ型に対しても期待通りに動作することが保証されます。
関連リンク
- Go言語
encoding/xml
パッケージのドキュメント: https://pkg.go.dev/encoding/xml
参考にした情報源リンク
- Go言語
encoding/xml
パッケージのchardata
に関する解説:- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF1n7wQB7HQzyD_hLHhtdbs8JbONmpqLig5gGf12UFayeWTbYL9b5uRURiFw5QmU4pTRQBu91qThP-y4GyccSJZsBt-x3clo358Q_pg3h2dhjAi5_NiPe1RYb4=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHBBC4-d7cYtLXFmjdFdUTIxNgMtis-C7Gi7YYXVZO2hWOx7l3LUA8NGx9BaPvHH8yOImBDcV_vp5eK7cgiRFYak4mz-9eQiHsv-vSMAHiu8ICEQ78vFBGMwDtRb-GxxPKm22IQSaUg3j4sAhrSLdL0dUDFeH5QxEX9MMoxCG8L77NtLCJ89oZ2FiE9OsJ-NAdWvJAw9fDcgOY=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFTgSZ4arpc80G9ZIRYwBp5q3vcVA1S0c6GxRSkq0IlccpiqjLS--9PSdg1CubOst0m7TF2bksSRFW-EYtmC-X990lKA5bHT8C4FC8L_PfD-xljtOXArcz4tcKtpUlKJVhi--l_xg4gv3fqILBS-A==
- Go言語 Issue 4506 に関する検索結果 (直接的なIssueは見つからず):
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGoQGX7dzKzFsWPiznnbk25KRypAzBXPaRaisI7iCds0FL0Asz0N7BKR1Y_GNtFW05Gqqk_4E4HHYlH6KuYSvgO66tCLIuqbYvxNk0InSyywZ4KiOPsccLkhTY3rY-u_ZBwdSCP
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGAnC_39q-H0DypadVCWatGNNMTzpTezA3JKXihzfMTFI3txJgy_JnNUbP9txAvzysot1ap0eIt1puwSEFVnL-ksE8YGHwRhlv3zTaijUX666vjSsBhUNzwNuH-5AM9KtHvSPJlJ7twvQ==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEmHkTUIcYme4IprbD214bJuhTJI2ch_qbvWLyAxkHOxkU8n_6uRSZkyY7IJXHwxE43l2eJYM3wydY6Z5nFnmNGJ19d-cxgaGvxHWjpGxciJMInI0q5VptHiTtLOMPHFL76AO_8
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEZQAYO1vmkt1slBgLDu8YHxrXhsI5LArp3fpMhE28AHlQ5yGaUh1nxoOsM0L3e4ryH8akeXwATGuLJrfT_0pU7eeMM7iWvwrSmb1rr7roxUqa_wiqFGOUbz_9AAodN