[インデックス 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