CodeSampleX

샘플

google.golang.org/protobuf v1.34.2: Pack and unpack heterogeneous Protocol Buffers messages using anypb New, UnmarshalTo, UnmarshalNew, and MessageIs with type URL validation

검증된 샘플 — golang google.golang.org/protobuf v1.34.2: Pack and unpack heterogeneous Protocol Buffers messages using anypb New, UnmarshalTo, UnmarshalNew, and…

sha256:d59418deed92e0442ff16c8406fd12a175ea23d3519018567218bf249fcc68c8

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

실행 증거

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

증거 기준
서명된 컨트랙트 통과
검증 영수증
1
빌드한 서명 키
1
선언된 환경 linux 24 · ubuntu · glibc 2.39 x64 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-27

케이스

HOW
목표
Pack and unpack heterogeneous Protocol Buffers messages using anypb New, UnmarshalTo, UnmarshalNew, and MessageIs with type URL validation
패키지
심벌
  • anypb.MessageIs
  • anypb.New
  • anypb.UnmarshalNew
  • anypb.UnmarshalTo
생성일
2026-08-27T10:20:06Z

컨트랙트

  1. anypb.New marshals a protobuf message into an Any message with the default TypeUrl prefix type.googleapis.com/ followed by the full message name, while anypb.New(nil) returns an error.
  2. any.MessageIs returns true when the target message descriptor matches the type URL regardless of prefix, and returns false when compared against a different message type or nil.
  3. any.UnmarshalTo unmarshals the payload into the destination message when the type matches, and returns an error without modifying destination when types mismatch.
  4. any.UnmarshalNew dynamically resolves the message type from protoregistry.GlobalTypes, instantiates the message, and unmarshals the payload, returning an error for unregistered types.
  5. anypb.MarshalFrom serializes messages into an existing Any struct with MarshalOptions, and anypb.UnmarshalNew supports custom protoregistry.Types resolvers via UnmarshalOptions.

파일

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

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

소스

NOTES.md
# Protocol Buffers Any Message Operations and Type Validation

A known-solution search for `google.golang.org/protobuf` identified existing samples for JSON formatting (`protojson`) and message manipulation semantics (`proto.Merge`, `proto.Clone`, `proto.Equal`, `proto.Reset`). A specific query for packing, unpacking, and type reflection with `google.protobuf.Any` (`anypb.New`, `anypb.UnmarshalTo`, `anypb.UnmarshalNew`, `anypb.MessageIs`) returned no safe match.

This sample tests and verifies heterogeneous message encapsulation behaviors in `google.golang.org/protobuf` pinned to release `v1.34.2`.

## Observed Behavior

1. **Packing Messages (`anypb.New`)**:
   - `anypb.New(m)` serializes a protobuf message into `anypb.Any`, constructing a canonical `TypeUrl` with the default prefix `type.googleapis.com/` followed by the fully-qualified message descriptor name (e.g. `type.googleapis.com/example.event.OrderCreated`).
   - Calling `anypb.New(nil)` does not return a nil `*anypb.Any`; it returns an error (`proto: invalid nil source message`).

2. **Type Checking (`any.MessageIs` / `any.MessageName`)**:
   - `any.MessageIs(m)` returns `true` if and only if `m` is non-nil and its descriptor full name matches the message type identified in `any.TypeUrl`.
   - `any.MessageIs` correctly handles custom domain prefixes in `TypeUrl` (e.g. `custom.domain.org/schemas/v1/...`) by matching against the suffix after the last slash.
   - Calling `any.MessageIs(nil)` returns `false`.
   - `any.MessageName()` returns the extracted `protoreflect.FullName` from `any.TypeUrl`.

3. **Explicit Destination Unpacking (`any.UnmarshalTo`)**:
   - `any.UnmarshalTo(dst)` unmarshals the payload into `dst` when the type names match.
   - When `dst` has a mismatched type descriptor, `UnmarshalTo` returns an error and leaves the destination message fields unmodified.

4. **Dynamic Registry Unpacking (`any.UnmarshalNew`)**:
   - `any.UnmarshalNew()` resolves the type name using the global type registry (`protoregistry.GlobalTypes`), instantiates a new message of that type (such as a dynamic message), and deserializes the payload into it.
   - If the type URL references an unregistered message type, `UnmarshalNew` fails with a resolution error.

5. **Custom Resolver and Explicit Marshaling (`anypb.MarshalFrom`, `anypb.UnmarshalNew`)**:
   - `anypb.MarshalFrom(dst, src, opts)` enables marshaling with explicit options (such as deterministic ordering).
   - Passing a custom `protoregistry.Types` resolver via `proto.UnmarshalOptions{Resolver: ...}` to `anypb.UnmarshalNew` allows scoped type resolution without relying on global state.
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: Pack and unpack heterogeneous Protocol Buffers messages using anypb New, UnmarshalTo, UnmarshalNew, and MessageIs with type URL validation
Kind: HOW

Use EXACTLY these public packages and versions:
  - pkg:golang/google.golang.org/protobuf@v1.34.2
Demonstrate these symbols/APIs:
  - anypb.MessageIs
  - anypb.New
  - anypb.UnmarshalNew
  - anypb.UnmarshalTo

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.
any_test.go
package protoany_test

import (
	"sync"
	"testing"

	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protodesc"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/reflect/protoregistry"
	"google.golang.org/protobuf/types/descriptorpb"
	"google.golang.org/protobuf/types/dynamicpb"
	"google.golang.org/protobuf/types/known/anypb"
)

var (
	initOnce    sync.Once
	orderDesc   protoreflect.MessageDescriptor
	paymentDesc protoreflect.MessageDescriptor
)

func initSchema() {
	initOnce.Do(func() {
		fd := &descriptorpb.FileDescriptorProto{
			Name:    proto.String("events.proto"),
			Package: proto.String("example.event"),
			Syntax:  proto.String("proto3"),
			MessageType: []*descriptorpb.DescriptorProto{
				{
					Name: proto.String("OrderCreated"),
					Field: []*descriptorpb.FieldDescriptorProto{
						{
							Name:     proto.String("order_id"),
							Number:   proto.Int32(1),
							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
							JsonName: proto.String("orderId"),
						},
						{
							Name:     proto.String("amount"),
							Number:   proto.Int32(2),
							Type:     descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(),
							JsonName: proto.String("amount"),
						},
					},
				},
				{
					Name: proto.String("PaymentProcessed"),
					Field: []*descriptorpb.FieldDescriptorProto{
						{
							Name:     proto.String("payment_id"),
							Number:   proto.Int32(1),
							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
							JsonName: proto.String("paymentId"),
						},
						{
							Name:     proto.String("status"),
							Number:   proto.Int32(2),
							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
							JsonName: proto.String("status"),
						},
					},
				},
			},
		}

		fileDesc, err := protodesc.NewFile(fd, protoregistry.GlobalFiles)
		if err != nil {
			panic(err)
		}

		orderDesc = fileDesc.Messages().ByName("OrderCreated")
		paymentDesc = fileDesc.Messages().ByName("PaymentProcessed")
		if orderDesc == nil || paymentDesc == nil {
			panic("failed to resolve descriptors")
		}

		// Register in GlobalTypes so anypb.UnmarshalNew can resolve them dynamically
		if err := protoregistry.GlobalTypes.RegisterMessage(dynamicpb.NewMessageType(orderDesc)); err != nil {
			panic(err)
		}
		if err := protoregistry.GlobalTypes.RegisterMessage(dynamicpb.NewMessageType(paymentDesc)); err != nil {
			panic(err)
		}
	})
}

func TestAnyPackAndMessageIs(t *testing.T) {
	initSchema()

	orderMsg := dynamicpb.NewMessage(orderDesc)
	orderMsg.Set(orderDesc.Fields().ByName("order_id"), protoreflect.ValueOfString("ord-12345"))
	orderMsg.Set(orderDesc.Fields().ByName("amount"), protoreflect.ValueOfInt64(9900))

	// 1. anypb.New serializes message and constructs TypeUrl with default prefix "type.googleapis.com/"
	anyMsg, err := anypb.New(orderMsg)
	if err != nil {
		t.Fatalf("anypb.New failed: %v", err)
	}
	expectedURL := "type.googleapis.com/example.event.OrderCreated"
	if anyMsg.GetTypeUrl() != expectedURL {
		t.Fatalf("expected TypeUrl %q, got %q", expectedURL, anyMsg.GetTypeUrl())
	}
	if len(anyMsg.GetValue()) == 0 {
		t.Fatalf("expected non-empty marshaled bytes in anyMsg.Value")
	}

	// 2. anypb.New(nil) returns an error rather than a nil Any
	nilAny, err := anypb.New(nil)
	if err == nil {
		t.Fatalf("expected anypb.New(nil) to return error, got Any: %v", nilAny)
	}

	// 3. MessageName extracts the fully qualified message name from TypeUrl
	if anyMsg.MessageName() != protoreflect.FullName("example.event.OrderCreated") {
		t.Fatalf("expected MessageName 'example.event.OrderCreated', got %q", anyMsg.MessageName())
	}

	// 4. MessageIs returns true when the target descriptor full name matches
	if !anyMsg.MessageIs(orderMsg) {
		t.Fatalf("expected anyMsg.MessageIs(orderMsg) to be true")
	}

	// 5. MessageIs returns false when tested against a different message type
	paymentMsg := dynamicpb.NewMessage(paymentDesc)
	if anyMsg.MessageIs(paymentMsg) {
		t.Fatalf("expected anyMsg.MessageIs(paymentMsg) to be false")
	}

	// 6. MessageIs returns false when passed nil
	if anyMsg.MessageIs(nil) {
		t.Fatalf("expected anyMsg.MessageIs(nil) to be false")
	}

	// 7. MessageIs matches regardless of custom TypeURLPrefix
	customAny := &anypb.Any{
		TypeUrl: "custom.domain.org/schemas/v1/example.event.OrderCreated",
		Value:   anyMsg.GetValue(),
	}
	if !customAny.MessageIs(orderMsg) {
		t.Fatalf("expected customAny.MessageIs(orderMsg) to match with custom URL prefix")
	}
	if customAny.MessageName() != protoreflect.FullName("example.event.OrderCreated") {
		t.Fatalf("expected customAny.MessageName() to be 'example.event.OrderCreated'")
	}
}

func TestAnyUnmarshalTo(t *testing.T) {
	initSchema()

	orderMsg := dynamicpb.NewMessage(orderDesc)
	orderMsg.Set(orderDesc.Fields().ByName("order_id"), protoreflect.ValueOfString("ord-999"))
	orderMsg.Set(orderDesc.Fields().ByName("amount"), protoreflect.ValueOfInt64(4500))

	anyMsg, err := anypb.New(orderMsg)
	if err != nil {
		t.Fatalf("anypb.New failed: %v", err)
	}

	// 1. Successful UnmarshalTo into matching target message
	targetOrder := dynamicpb.NewMessage(orderDesc)
	if err := anyMsg.UnmarshalTo(targetOrder); err != nil {
		t.Fatalf("UnmarshalTo failed: %v", err)
	}
	if got := targetOrder.Get(orderDesc.Fields().ByName("order_id")).String(); got != "ord-999" {
		t.Fatalf("expected order_id 'ord-999', got %q", got)
	}
	if got := targetOrder.Get(orderDesc.Fields().ByName("amount")).Int(); got != 4500 {
		t.Fatalf("expected amount 4500, got %d", got)
	}

	// 2. UnmarshalTo fails with type mismatch error when target type descriptor does not match TypeUrl
	targetPayment := dynamicpb.NewMessage(paymentDesc)
	targetPayment.Set(paymentDesc.Fields().ByName("payment_id"), protoreflect.ValueOfString("pay-initial"))
	if err := anyMsg.UnmarshalTo(targetPayment); err == nil {
		t.Fatalf("expected error when unmarshalling into mismatched type, got nil")
	}
	// Target message is left untouched on type mismatch error
	if got := targetPayment.Get(paymentDesc.Fields().ByName("payment_id")).String(); got != "pay-initial" {
		t.Fatalf("target was modified despite type mismatch error")
	}
}

func TestAnyUnmarshalNew(t *testing.T) {
	initSchema()

	orderMsg := dynamicpb.NewMessage(orderDesc)
	orderMsg.Set(orderDesc.Fields().ByName("order_id"), protoreflect.ValueOfString("ord-dyn-777"))
	orderMsg.Set(orderDesc.Fields().ByName("amount"), protoreflect.ValueOfInt64(1200))

	anyMsg, err := anypb.New(orderMsg)
	if err != nil {
		t.Fatalf("anypb.New failed: %v", err)
	}

	// 1. UnmarshalNew resolves type from global registry, constructs dynamic/concrete message and populates fields
	unmarshaled, err := anyMsg.UnmarshalNew()
	if err != nil {
		t.Fatalf("anyMsg.UnmarshalNew failed: %v", err)
	}
	dynMsg, ok := unmarshaled.(*dynamicpb.Message)
	if !ok {
		t.Fatalf("expected dynamicpb.Message, got %T", unmarshaled)
	}
	if dynMsg.Get(orderDesc.Fields().ByName("order_id")).String() != "ord-dyn-777" {
		t.Fatalf("unexpected order_id: %s", dynMsg.Get(orderDesc.Fields().ByName("order_id")).String())
	}
	if dynMsg.Get(orderDesc.Fields().ByName("amount")).Int() != 1200 {
		t.Fatalf("unexpected amount: %d", dynMsg.Get(orderDesc.Fields().ByName("amount")).Int())
	}

	// 2. UnmarshalNew fails when TypeUrl names an unregistered type
	unknownAny := &anypb.Any{
		TypeUrl: "type.googleapis.com/unregistered.package.UnknownMessage",
		Value:   []byte{0x08, 0x01},
	}
	_, err = unknownAny.UnmarshalNew()
	if err == nil {
		t.Fatalf("expected UnmarshalNew to fail on unregistered type, got nil error")
	}
}

func TestAnyCustomResolverAndMarshalFrom(t *testing.T) {
	initSchema()

	paymentMsg := dynamicpb.NewMessage(paymentDesc)
	paymentMsg.Set(paymentDesc.Fields().ByName("payment_id"), protoreflect.ValueOfString("pay-custom-888"))
	paymentMsg.Set(paymentDesc.Fields().ByName("status"), protoreflect.ValueOfString("SETTLED"))

	// 1. anypb.MarshalFrom marshals into an existing destination Any struct with MarshalOptions
	var dstAny anypb.Any
	if err := anypb.MarshalFrom(&dstAny, paymentMsg, proto.MarshalOptions{Deterministic: true}); err != nil {
		t.Fatalf("anypb.MarshalFrom failed: %v", err)
	}
	if dstAny.GetTypeUrl() != "type.googleapis.com/example.event.PaymentProcessed" {
		t.Fatalf("unexpected TypeUrl from MarshalFrom: %s", dstAny.GetTypeUrl())
	}

	// 2. Using a custom Types resolver with anypb.UnmarshalNew
	customTypes := new(protoregistry.Types)
	if err := customTypes.RegisterMessage(dynamicpb.NewMessageType(paymentDesc)); err != nil {
		t.Fatalf("RegisterMessage on custom resolver failed: %v", err)
	}

	unmarshaled, err := anypb.UnmarshalNew(&dstAny, proto.UnmarshalOptions{Resolver: customTypes})
	if err != nil {
		t.Fatalf("anypb.UnmarshalNew with custom resolver failed: %v", err)
	}
	if unmarshaled.ProtoReflect().Get(paymentDesc.Fields().ByName("payment_id")).String() != "pay-custom-888" {
		t.Fatalf("unexpected payment_id from custom resolver unmarshal")
	}

	// 3. Resolver lacking the type fails resolution with an error
	emptyResolver := new(protoregistry.Types)
	_, err = anypb.UnmarshalNew(&dstAny, proto.UnmarshalOptions{Resolver: emptyResolver})
	if err == nil {
		t.Fatalf("expected empty custom resolver to fail resolution")
	}
}
csx.json
{"case":{"caseId":"case:sha256:5afd88eade6ebd51c7758fb7bc0a346a5ad0b6181363d0bdf783f729e640942f","contract":["anypb.New marshals a protobuf message into an Any message with the default TypeUrl prefix type.googleapis.com/ followed by the full message name, while anypb.New(nil) returns an error.","any.MessageIs returns true when the target message descriptor matches the type URL regardless of prefix, and returns false when compared against a different message type or nil.","any.UnmarshalTo unmarshals the payload into the destination message when the type matches, and returns an error without modifying destination when types mismatch.","any.UnmarshalNew dynamically resolves the message type from protoregistry.GlobalTypes, instantiates the message, and unmarshals the payload, returning an error for unregistered types.","anypb.MarshalFrom serializes messages into an existing Any struct with MarshalOptions, and anypb.UnmarshalNew supports custom protoregistry.Types resolvers via UnmarshalOptions."],"goal":"Pack and unpack heterogeneous Protocol Buffers messages using anypb New, UnmarshalTo, UnmarshalNew, and MessageIs with type URL validation","kind":"HOW","packages":["pkg:golang/google.golang.org/protobuf@v1.34.2"],"schemaVersion":1,"symbols":["anypb.MessageIs","anypb.New","anypb.UnmarshalNew","anypb.UnmarshalTo"]},"contractCommand":["go","test","./..."],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"golang","libc":"glibc","libcVersion":"2.39","os":"linux","osVersionBucket":"24","packageManager":"go","schemaVersion":1},"license":"MIT-0","packages":["pkg:golang/google.golang.org/protobuf@v1.34.2"],"schemaVersion":1,"subject":"pkg:golang/google.golang.org/protobuf@v1.34.2","symbols":["anypb.MessageIs","anypb.New","anypb.UnmarshalNew","anypb.UnmarshalTo"],"verifierAdapter":"golang@1"}
go.mod
module example.com/protoany

go 1.26.6

require google.golang.org/protobuf v1.34.2
go.sum
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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": "Pack and unpack heterogeneous Protocol Buffers messages using anypb New, UnmarshalTo, UnmarshalNew, and MessageIs with type URL validation",
  "kind": "HOW",
  "packages": [
    "pkg:golang/google.golang.org/protobuf@v1.34.2"
  ],
  "symbols": [
    "anypb.MessageIs",
    "anypb.New",
    "anypb.UnmarshalNew",
    "anypb.UnmarshalTo"
  ]
}

오리진 시더

익명