CodeSampleX

샘플

google.golang.org/protobuf v1.32.0: Convert between dynamic Go data structures and Protocol Buffers Struct, ListValue, and Value with structpb

검증된 샘플 — golang google.golang.org/protobuf v1.32.0: Convert between dynamic Go data structures and Protocol Buffers Struct, ListValue, and Value with…

sha256:5da721c1166d04ed80369d75435c1584df3cd3d121055f0c4872d4ebf8d74090

이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다. 통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다. MIT-0

실행 증거

선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.

증거 기준
서명된 컨트랙트 통과
검증 영수증
1
빌드한 서명 키
1
선언된 환경 go linux 24 · ubuntu · glibc 2.39 x64 go go go

검증 실행 환경

환경 컨트랙트 단계 실행일
go 1.26 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · golang@1golang:1.26-alpine@sha256:28d89ee9cc0f…
2026-08-28

케이스

HOW
목표
Convert between dynamic Go data structures and Protocol Buffers Struct, ListValue, and Value with structpb
패키지
심벌
  • google.golang.org/protobuf/types/known/structpb.ListValue.AsSlice
  • google.golang.org/protobuf/types/known/structpb.NewList
  • google.golang.org/protobuf/types/known/structpb.NewStruct
  • google.golang.org/protobuf/types/known/structpb.NewValue
  • google.golang.org/protobuf/types/known/structpb.Struct.AsMap
  • google.golang.org/protobuf/types/known/structpb.Value.AsInterface
환경
go
생성일
2026-08-28T09:04:35Z

컨트랙트

  1. structpb.NewValue converts Go primitives (nil, bool, int/int32/int64/float32/float64, string) to appropriate Value kinds with numeric types represented as float64 NumberValue.
  2. structpb.NewStruct converts Go maps into *structpb.Struct, and Struct.AsMap converts it back to map[string]interface{}, while calling AsMap on a nil receiver safely returns an empty non-nil map.
  3. structpb.NewList converts Go slices into *structpb.ListValue, and ListValue.AsSlice converts it back to []interface{}, while calling AsSlice on a nil receiver safely returns an empty non-nil slice.
  4. structpb.Value.AsInterface recursively unpacks nested StructValue and ListValue into corresponding Go map and slice representations, and calling AsInterface on a nil receiver safely returns nil.
  5. structpb.NewValue returns an error when passed unsupported Go types such as channels or functions, and NewStruct/NewList propagate conversion failures for nested unsupported types.
  6. structpb.Struct supports proto binary wire serialization and protojson serialization formatting as a standard JSON object.

파일

  • NOTES.md
  • PROMPT.md
  • csx.json
  • go.mod
  • go.sum
  • spec.json
  • structpb_test.go

소스 아티팩트 내려받기 (tar.gz)

소스

NOTES.md
# Protocol Buffers structpb Dynamic Value Conversion

A search for dynamic Go data structure conversion using `structpb` in `google.golang.org/protobuf@v1.32.0` returned `NO_SAFE_MATCH`.

This sample tests and verifies conversion semantics between dynamic Go types (primitives, maps, slices) and Protocol Buffers `Struct`, `ListValue`, and `Value` messages.

## Observed Behavior

1. **Primitive Value Conversion (`structpb.NewValue`)**:
   - `structpb.NewValue(nil)` produces a `*structpb.Value` with `NullValue_NULL_VALUE`. Calling `AsInterface()` on it returns `nil`.
   - `structpb.NewValue(bool)` maps to `BoolValue`.
   - Integer types (`int`, `int32`, `int64`) and float types (`float32`, `float64`) map to `NumberValue` and are normalized as `float64`.
   - String values map to `StringValue`.

2. **Map Conversion (`structpb.NewStruct` & `Struct.AsMap`)**:
   - `structpb.NewStruct(map[string]interface{})` converts Go maps to `*structpb.Struct`.
   - `st.AsMap()` extracts the values back into a `map[string]interface{}`.
   - Calling `AsMap()` on a `nil` receiver safely returns an empty non-nil `map[string]interface{}{}` without panicking.
   - `structpb.NewStruct(nil)` succeeds and returns an empty `Struct`.

3. **Slice Conversion (`structpb.NewList` & `ListValue.AsSlice`)**:
   - `structpb.NewList([]interface{})` converts Go slices to `*structpb.ListValue`.
   - `listVal.AsSlice()` converts the elements back into `[]interface{}`.
   - Calling `AsSlice()` on a `nil` receiver safely returns an empty non-nil `[]interface{}{}` without panicking.
   - `structpb.NewList(nil)` succeeds and returns an empty `ListValue`.

4. **Recursive Nested Unpacking (`structpb.Value.AsInterface`)**:
   - `Value.AsInterface()` recursively unpacks nested structs into Go maps and nested lists into Go slices.
   - Calling `AsInterface()` on a `nil` receiver safely returns `nil`.

5. **Error Handling on Unsupported Types**:
   - Types that cannot be represented in JSON / protobuf Struct (e.g. channels, functions) return an error from `structpb.NewValue`.
   - Nested unsupported types within maps or slices cause `structpb.NewStruct` or `structpb.NewList` to fail with an error.

6. **Serialization & Interoperability**:
   - `structpb.Struct` supports both binary protobuf wire serialization (`proto.Marshal` / `proto.Unmarshal`) and JSON serialization (`protojson.Marshal` / `protojson.Unmarshal`), serializing directly as a standard JSON object.
PROMPT.md
Clean-room public code sample — generation instructions

Write a brand-new, minimal, self-contained code sample in this clean-room directory.
Do not copy, paraphrase, or reference any existing project source. Work only from this spec.

A csx.json manifest scaffold already exists. Do not recreate it from memory. Preserve its case.goal, packages and symbols; fill its empty case.contract with exact assertions and correct its environment, commands and verifierAdapter for the files you generate.

Goal: Convert between dynamic Go data structures and Protocol Buffers Struct, ListValue, and Value with structpb
Kind: HOW

Use EXACTLY these public packages and versions:
  - pkg:golang/google.golang.org/protobuf@v1.32.0
Demonstrate these symbols/APIs:
  - google.golang.org/protobuf/types/known/structpb.ListValue.AsSlice
  - google.golang.org/protobuf/types/known/structpb.NewList
  - google.golang.org/protobuf/types/known/structpb.NewStruct
  - google.golang.org/protobuf/types/known/structpb.NewValue
  - google.golang.org/protobuf/types/known/structpb.Struct.AsMap
  - google.golang.org/protobuf/types/known/structpb.Value.AsInterface

Rules:
  - One focused purpose; the smallest project that proves the goal.
  - Include a contract test (test/contract.*) that runs OFFLINE and exits 0 exactly when the goal behavior works.
  - Pin every dependency with a lockfile so resolution is reproducible.
  - No secrets, credentials, or tokens. No real URLs (only example.com or localhost). No absolute paths.
  - No personal names, emails, company names, or project identifiers of any kind.
  - No binaries and no generated output (node_modules, dist, target, venv, .git, .env).
  - Keep it under 200 files and 256KB packed.
csx.json
{"case":{"caseId":"case:sha256:e798699eb41f7a4f3f5388da7cc9e2b3de7df159eb0cc3ffff9b99988cadfe98","contract":["structpb.NewValue converts Go primitives (nil, bool, int/int32/int64/float32/float64, string) to appropriate Value kinds with numeric types represented as float64 NumberValue.","structpb.NewStruct converts Go maps into *structpb.Struct, and Struct.AsMap converts it back to map[string]interface{}, while calling AsMap on a nil receiver safely returns an empty non-nil map.","structpb.NewList converts Go slices into *structpb.ListValue, and ListValue.AsSlice converts it back to []interface{}, while calling AsSlice on a nil receiver safely returns an empty non-nil slice.","structpb.Value.AsInterface recursively unpacks nested StructValue and ListValue into corresponding Go map and slice representations, and calling AsInterface on a nil receiver safely returns nil.","structpb.NewValue returns an error when passed unsupported Go types such as channels or functions, and NewStruct/NewList propagate conversion failures for nested unsupported types.","structpb.Struct supports proto binary wire serialization and protojson serialization formatting as a standard JSON object."],"goal":"Convert between dynamic Go data structures and Protocol Buffers Struct, ListValue, and Value with structpb","kind":"HOW","packages":["pkg:golang/google.golang.org/protobuf@v1.32.0"],"schemaVersion":1,"symbols":["google.golang.org/protobuf/types/known/structpb.ListValue.AsSlice","google.golang.org/protobuf/types/known/structpb.NewList","google.golang.org/protobuf/types/known/structpb.NewStruct","google.golang.org/protobuf/types/known/structpb.NewValue","google.golang.org/protobuf/types/known/structpb.Struct.AsMap","google.golang.org/protobuf/types/known/structpb.Value.AsInterface"]},"contractCommand":["go","test","./..."],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"golang","executionContext":"go","language":"go","libc":"glibc","libcVersion":"2.39","os":"linux","osVersionBucket":"24","packageManager":"go","runtime":"go","schemaVersion":1},"license":"MIT-0","packages":["pkg:golang/google.golang.org/protobuf@v1.32.0"],"schemaVersion":1,"subject":"pkg:golang/google.golang.org/protobuf@v1.32.0","symbols":["google.golang.org/protobuf/types/known/structpb.ListValue.AsSlice","google.golang.org/protobuf/types/known/structpb.NewList","google.golang.org/protobuf/types/known/structpb.NewStruct","google.golang.org/protobuf/types/known/structpb.NewValue","google.golang.org/protobuf/types/known/structpb.Struct.AsMap","google.golang.org/protobuf/types/known/structpb.Value.AsInterface"],"verifierAdapter":"golang@1"}
go.mod
module example.com/structpbdemo

go 1.23

require google.golang.org/protobuf v1.36.12
go.sum
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
spec.json
{
  "schemaVersion": 1,
  "goal": "Convert between dynamic Go data structures and Protocol Buffers Struct, ListValue, and Value with structpb",
  "kind": "HOW",
  "packages": [
    "pkg:golang/google.golang.org/protobuf@v1.32.0"
  ],
  "symbols": [
    "google.golang.org/protobuf/types/known/structpb.ListValue.AsSlice",
    "google.golang.org/protobuf/types/known/structpb.NewList",
    "google.golang.org/protobuf/types/known/structpb.NewStruct",
    "google.golang.org/protobuf/types/known/structpb.NewValue",
    "google.golang.org/protobuf/types/known/structpb.Struct.AsMap",
    "google.golang.org/protobuf/types/known/structpb.Value.AsInterface"
  ]
}
structpb_test.go
package structpbdemo_test

import (
	"reflect"
	"testing"

	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/structpb"
)

// TestStructPBNewValuePrimitives verifies structpb.NewValue conversion from Go primitives,
// ensuring types map to appropriate Value kinds and AsInterface() round-trips accurately.
func TestStructPBNewValuePrimitives(t *testing.T) {
	// 1. Nil / NullValue
	vNil, err := structpb.NewValue(nil)
	if err != nil {
		t.Fatalf("NewValue(nil) failed: %v", err)
	}
	if _, ok := vNil.GetKind().(*structpb.Value_NullValue); !ok {
		t.Fatalf("expected NullValue kind, got %T", vNil.GetKind())
	}
	if got := vNil.AsInterface(); got != nil {
		t.Fatalf("expected AsInterface() for nil value to return nil, got %#v", got)
	}

	// 2. Bool
	vBool, err := structpb.NewValue(true)
	if err != nil {
		t.Fatalf("NewValue(true) failed: %v", err)
	}
	if got, ok := vBool.GetKind().(*structpb.Value_BoolValue); !ok || !got.BoolValue {
		t.Fatalf("expected BoolValue(true), got %v", vBool.GetKind())
	}
	if got := vBool.AsInterface(); got != true {
		t.Fatalf("expected AsInterface() to return true, got %v", got)
	}

	// 3. Numbers: integers and floats are converted to float64 NumberValue
	numbers := []interface{}{
		int(42),
		int32(100),
		int64(9999),
		float32(3.14),
		float64(2.71828),
	}
	for _, num := range numbers {
		vNum, err := structpb.NewValue(num)
		if err != nil {
			t.Fatalf("NewValue(%T(%v)) failed: %v", num, num, err)
		}
		if _, ok := vNum.GetKind().(*structpb.Value_NumberValue); !ok {
			t.Fatalf("expected NumberValue kind for %T, got %T", num, vNum.GetKind())
		}
		if _, ok := vNum.AsInterface().(float64); !ok {
			t.Fatalf("expected AsInterface() to return float64 for %T, got %T", num, vNum.AsInterface())
		}
	}

	// 4. String
	vStr, err := structpb.NewValue("test-string")
	if err != nil {
		t.Fatalf("NewValue(string) failed: %v", err)
	}
	if got, ok := vStr.GetKind().(*structpb.Value_StringValue); !ok || got.StringValue != "test-string" {
		t.Fatalf("expected StringValue('test-string'), got %v", vStr.GetKind())
	}
	if got := vStr.AsInterface(); got != "test-string" {
		t.Fatalf("expected AsInterface() to return 'test-string', got %v", got)
	}
}

// TestStructPBNewStructAndAsMap verifies structpb.NewStruct converts Go maps into *structpb.Struct
// and Struct.AsMap() converts it back to map[string]interface{}.
func TestStructPBNewStructAndAsMap(t *testing.T) {
	inputMap := map[string]interface{}{
		"name":    "service-config",
		"port":    float64(8080),
		"enabled": true,
		"tags":    []interface{}{"api", "gateway"},
		"extra":   nil,
	}

	st, err := structpb.NewStruct(inputMap)
	if err != nil {
		t.Fatalf("NewStruct failed: %v", err)
	}

	if st.Fields == nil {
		t.Fatalf("expected non-nil Fields in Struct")
	}

	// Verify fields
	if nameVal := st.Fields["name"].GetStringValue(); nameVal != "service-config" {
		t.Errorf("expected name to be 'service-config', got %q", nameVal)
	}
	if portVal := st.Fields["port"].GetNumberValue(); portVal != 8080 {
		t.Errorf("expected port to be 8080, got %v", portVal)
	}
	if enabledVal := st.Fields["enabled"].GetBoolValue(); !enabledVal {
		t.Errorf("expected enabled to be true, got %v", enabledVal)
	}

	// Convert back via AsMap()
	outputMap := st.AsMap()
	if !reflect.DeepEqual(inputMap, outputMap) {
		t.Fatalf("AsMap() mismatch:\nwant: %#v\ngot:  %#v", inputMap, outputMap)
	}

	// Nil struct safety: calling AsMap() on nil receiver safely returns empty map (not nil)
	var nilStruct *structpb.Struct
	if got := nilStruct.AsMap(); got == nil || len(got) != 0 {
		t.Fatalf("expected nilStruct.AsMap() to return empty non-nil map, got %#v", got)
	}

	// Empty struct
	emptyStruct, err := structpb.NewStruct(nil)
	if err != nil {
		t.Fatalf("NewStruct(nil) failed: %v", err)
	}
	if len(emptyStruct.AsMap()) != 0 {
		t.Fatalf("expected empty map for NewStruct(nil), got %#v", emptyStruct.AsMap())
	}
}

// TestStructPBNewListAndAsSlice verifies structpb.NewList converts Go slices into *structpb.ListValue
// and ListValue.AsSlice() converts it back to []interface{}.
func TestStructPBNewListAndAsSlice(t *testing.T) {
	inputSlice := []interface{}{
		"first",
		float64(42),
		false,
		map[string]interface{}{"nested_key": "nested_val"},
		nil,
	}

	listVal, err := structpb.NewList(inputSlice)
	if err != nil {
		t.Fatalf("NewList failed: %v", err)
	}

	if len(listVal.Values) != len(inputSlice) {
		t.Fatalf("expected %d values, got %d", len(inputSlice), len(listVal.Values))
	}

	outputSlice := listVal.AsSlice()
	if !reflect.DeepEqual(inputSlice, outputSlice) {
		t.Fatalf("AsSlice() mismatch:\nwant: %#v\ngot:  %#v", inputSlice, outputSlice)
	}

	// Nil list safety: calling AsSlice() on nil receiver safely returns empty slice (not nil)
	var nilList *structpb.ListValue
	if got := nilList.AsSlice(); got == nil || len(got) != 0 {
		t.Fatalf("expected nilList.AsSlice() to return empty non-nil slice, got %#v", got)
	}

	// Empty list
	emptyList, err := structpb.NewList(nil)
	if err != nil {
		t.Fatalf("NewList(nil) failed: %v", err)
	}
	if len(emptyList.AsSlice()) != 0 {
		t.Fatalf("expected empty slice for NewList(nil), got %#v", emptyList.AsSlice())
	}
}

// TestStructPBNestedValueAsInterface verifies nested struct and list value conversions via Value.AsInterface().
func TestStructPBNestedValueAsInterface(t *testing.T) {
	nestedData := map[string]interface{}{
		"cluster": "us-east-1",
		"nodes": []interface{}{
			map[string]interface{}{"id": "node-1", "active": true},
			map[string]interface{}{"id": "node-2", "active": false},
		},
	}

	val, err := structpb.NewValue(nestedData)
	if err != nil {
		t.Fatalf("NewValue(nestedData) failed: %v", err)
	}

	if _, ok := val.GetKind().(*structpb.Value_StructValue); !ok {
		t.Fatalf("expected StructValue kind, got %T", val.GetKind())
	}

	gotInterface := val.AsInterface()
	if !reflect.DeepEqual(nestedData, gotInterface) {
		t.Fatalf("AsInterface() mismatch on nested data:\nwant: %#v\ngot:  %#v", nestedData, gotInterface)
	}

	// Nil value receiver safety
	var nilVal *structpb.Value
	if got := nilVal.AsInterface(); got != nil {
		t.Fatalf("expected nilVal.AsInterface() to return nil, got %#v", got)
	}
}

// TestStructPBUnsupportedTypesError verifies structpb.NewValue returns an error for unsupported Go types (e.g. channels, funcs).
func TestStructPBUnsupportedTypesError(t *testing.T) {
	// Channels cannot be converted
	ch := make(chan int)
	if _, err := structpb.NewValue(ch); err == nil {
		t.Fatalf("expected error when calling NewValue on chan, got nil")
	}

	// Functions cannot be converted
	fn := func() {}
	if _, err := structpb.NewValue(fn); err == nil {
		t.Fatalf("expected error when calling NewValue on func, got nil")
	}

	// Map with unsupported value returns error in NewStruct
	invalidMap := map[string]interface{}{
		"bad": ch,
	}
	if _, err := structpb.NewStruct(invalidMap); err == nil {
		t.Fatalf("expected error when calling NewStruct with channel value, got nil")
	}

	// Slice with unsupported element returns error in NewList
	invalidSlice := []interface{}{"ok", fn}
	if _, err := structpb.NewList(invalidSlice); err == nil {
		t.Fatalf("expected error when calling NewList with func value, got nil")
	}
}

// TestStructPBProtoAndJSONSerialization verifies proto wire and JSON serialization of structpb.Struct.
func TestStructPBProtoAndJSONSerialization(t *testing.T) {
	data := map[string]interface{}{
		"env":     "production",
		"retries": float64(3),
		"verbose": false,
	}

	st, err := structpb.NewStruct(data)
	if err != nil {
		t.Fatalf("NewStruct failed: %v", err)
	}

	// 1. Proto wire serialization
	wireBytes, err := proto.Marshal(st)
	if err != nil {
		t.Fatalf("proto.Marshal failed: %v", err)
	}

	unmarshaledSt := &structpb.Struct{}
	if err := proto.Unmarshal(wireBytes, unmarshaledSt); err != nil {
		t.Fatalf("proto.Unmarshal failed: %v", err)
	}

	if !proto.Equal(st, unmarshaledSt) {
		t.Fatalf("proto.Unmarshal result differs from original")
	}

	// 2. protojson serialization produces canonical JSON representation
	jsonBytes, err := protojson.Marshal(st)
	if err != nil {
		t.Fatalf("protojson.Marshal failed: %v", err)
	}

	jsonSt := &structpb.Struct{}
	if err := protojson.Unmarshal(jsonBytes, jsonSt); err != nil {
		t.Fatalf("protojson.Unmarshal failed: %v", err)
	}

	if !proto.Equal(st, jsonSt) {
		t.Fatalf("protojson roundtrip mismatch")
	}
}

오리진 시더

익명