CodeSampleX

サンプル

google.golang.org/protobuf v1.36.10: protojson.UnmarshalOptions

検証済みサンプル — golang google.golang.org/protobuf v1.36.10: protojson.UnmarshalOptions. go 1.26 · linux debian/x64 · docker で contract を実行し、成功しました.

sha256:4ed731dbfeadc3854f9ec9af234116089b61bec3a0c90cdd1ae8b21e47dcf60e

このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。 合格した契約受領証を提出した異なる署名鍵の数です。1 なら作者だけ、2 以上なら他の誰かもビルドしています。鍵は自己生成で背後に登録された身元がないため、数えているのは人ではなく鍵です。 MIT-0

実行証拠

宣言された環境と署名済みの実行を分けてあります。このサンプルが何をどこで実行したかをそのまま確認できます。

証拠の基準
署名済みコントラクト合格
検証レシート
1
ビルドした署名鍵
1
宣言された環境 go linux 24 · ubuntu · glibc 2.39 x64 go go go

検証実行環境

環境 コントラクト ステージ 実行日
go 1.26 · linux debian/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · golang@1golang:1.26@sha256:e30143be198a…
2026-09-02

ケース

HOW
ゴール
verify protojson.UnmarshalOptions in pkg:golang/google.golang.org/protobuf@v1.36.10
パッケージ
シンボル
  • protojson.UnmarshalOptions
環境
go
作成日
2026-09-02T00:47:34Z

コントラクト

  1. protojson.UnmarshalOptions.DiscardUnknown=true allows unmarshaling JSON payloads containing unknown fields without returning an error.
  2. protojson.UnmarshalOptions.DiscardUnknown=false returns an error when encountering unknown fields during unmarshaling.
  3. protojson.UnmarshalOptions.AllowPartial=true allows unmarshaling incomplete messages with missing required fields without returning an error.
  4. protojson.UnmarshalOptions.AllowPartial=false returns an error when unmarshaling messages that have missing required fields.
  5. protojson.UnmarshalOptions.RecursionLimit enforces maximum message nesting depth and returns an error when depth is exceeded.
  6. protojson.UnmarshalOptions.Resolver allows resolving custom Any message types using a custom protoregistry.Types resolver.

ファイル

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

ソースアーティファクトをダウンロード (tar.gz)

ソース

NOTES.md
# Protocol Buffers JSON Unmarshaling Options with protojson

This sample verifies the unmarshaling configurations and behaviors provided by `protojson.UnmarshalOptions` in `google.golang.org/protobuf/encoding/protojson` pinned to version `v1.36.10`.

## Verified Behaviors

1. **DiscardUnknown**:
   - `DiscardUnknown: false` (default): Fails with an error when unmarshaling JSON payloads containing unknown field names.
   - `DiscardUnknown: true`: Ignores unknown field names without error, correctly populating recognized fields.

2. **AllowPartial**:
   - `AllowPartial: false` (default): Fails validation when unmarshaling incomplete messages with missing required fields (proto2).
   - `AllowPartial: true`: Allows unmarshaling partial messages with missing required fields without returning an error.

3. **RecursionLimit**:
   - Limits the maximum depth of nested message parsing.
   - Unmarshaling JSON payloads that exceed the configured `RecursionLimit` returns an error.
   - Deeply nested structures within the limit unmarshal successfully.

4. **Resolver**:
   - `Resolver`: Custom `protoregistry.MessageTypeResolver` / `protoregistry.ExtensionTypeResolver` used to resolve `google.protobuf.Any` message types.
   - When a type is registered in the custom resolver, Any messages unmarshal and unpack correctly.
   - When missing from the resolver, unmarshaling returns an error indicating unresolved type.
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: verify protojson.UnmarshalOptions in pkg:golang/google.golang.org/protobuf@v1.36.10
Kind: HOW

Use EXACTLY these public packages and versions:
  - pkg:golang/google.golang.org/protobuf@v1.36.10
Demonstrate these symbols/APIs:
  - protojson.UnmarshalOptions
Constraints:
  - executionContext: node
Required runtime conditions:
  - ecosystem: npm
  - language: javascript
  - moduleSystem: cjs
  - packageManager: npm@10.9.8
  - runtime: node@22.23.2

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:b4402272de7f9b0329b138560c139208f9b1e3d32a0494a08f3b7a0c5231d1ae","contract":["protojson.UnmarshalOptions.DiscardUnknown=true allows unmarshaling JSON payloads containing unknown fields without returning an error.","protojson.UnmarshalOptions.DiscardUnknown=false returns an error when encountering unknown fields during unmarshaling.","protojson.UnmarshalOptions.AllowPartial=true allows unmarshaling incomplete messages with missing required fields without returning an error.","protojson.UnmarshalOptions.AllowPartial=false returns an error when unmarshaling messages that have missing required fields.","protojson.UnmarshalOptions.RecursionLimit enforces maximum message nesting depth and returns an error when depth is exceeded.","protojson.UnmarshalOptions.Resolver allows resolving custom Any message types using a custom protoregistry.Types resolver."],"goal":"verify protojson.UnmarshalOptions in pkg:golang/google.golang.org/protobuf@v1.36.10","kind":"HOW","packages":["pkg:golang/google.golang.org/protobuf@v1.36.10"],"schemaVersion":1,"symbols":["protojson.UnmarshalOptions"]},"contractCommand":["go","test","./..."],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"golang","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.36.10"],"schemaVersion":1,"subject":"pkg:golang/google.golang.org/protobuf@v1.36.10","symbols":["protojson.UnmarshalOptions"],"verifierAdapter":"golang@1"}
go.mod
module example.com/protojson_unmarshaloptions

go 1.23

require google.golang.org/protobuf v1.36.10
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.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
spec.json
{
  "schemaVersion": 1,
  "goal": "verify protojson.UnmarshalOptions in pkg:golang/google.golang.org/protobuf@v1.36.10",
  "kind": "HOW",
  "packages": [
    "pkg:golang/google.golang.org/protobuf@v1.36.10"
  ],
  "symbols": [
    "protojson.UnmarshalOptions"
  ],
  "constraints": {
    "executionContext": "node"
  },
  "runtimeConditions": {
    "ecosystem": "npm",
    "language": "javascript",
    "moduleSystem": "cjs",
    "packageManager": "npm@10.9.8",
    "runtime": "node@22.23.2"
  }
}
unmarshal_options_test.go
package main

import (
	"strings"
	"testing"

	"google.golang.org/protobuf/encoding/protojson"
	"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"
)

func buildDescriptors(t *testing.T) *protoregistry.Files {
	t.Helper()

	// 1. Proto3 User & Profile schema
	proto3File := &descriptorpb.FileDescriptorProto{
		Name:    proto.String("user.proto"),
		Syntax:  proto.String("proto3"),
		Package: proto.String("sample.user"),
		MessageType: []*descriptorpb.DescriptorProto{
			{
				Name: proto.String("Profile"),
				Field: []*descriptorpb.FieldDescriptorProto{
					{
						Name:     proto.String("bio"),
						Number:   proto.Int32(1),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
						JsonName: proto.String("bio"),
					},
					{
						Name:     proto.String("website"),
						Number:   proto.Int32(2),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
						JsonName: proto.String("website"),
					},
				},
			},
			{
				Name: proto.String("User"),
				Field: []*descriptorpb.FieldDescriptorProto{
					{
						Name:     proto.String("user_id"),
						Number:   proto.Int32(1),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(),
						JsonName: proto.String("userId"),
					},
					{
						Name:     proto.String("name"),
						Number:   proto.Int32(2),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
						JsonName: proto.String("name"),
					},
					{
						Name:     proto.String("tags"),
						Number:   proto.Int32(3),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
						JsonName: proto.String("tags"),
					},
					{
						Name:     proto.String("profile"),
						Number:   proto.Int32(4),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
						TypeName: proto.String(".sample.user.Profile"),
						JsonName: proto.String("profile"),
					},
				},
			},
		},
	}

	// 2. Proto2 Item with required fields
	proto2File := &descriptorpb.FileDescriptorProto{
		Name:    proto.String("item.proto"),
		Syntax:  proto.String("proto2"),
		Package: proto.String("sample.item"),
		MessageType: []*descriptorpb.DescriptorProto{
			{
				Name: proto.String("RequiredItem"),
				Field: []*descriptorpb.FieldDescriptorProto{
					{
						Name:     proto.String("id"),
						Number:   proto.Int32(1),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
						JsonName: proto.String("id"),
					},
					{
						Name:     proto.String("title"),
						Number:   proto.Int32(2),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
						JsonName: proto.String("title"),
					},
					{
						Name:     proto.String("description"),
						Number:   proto.Int32(3),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
						JsonName: proto.String("description"),
					},
				},
			},
		},
	}

	// 3. Proto3 Recursive Node schema
	recursiveFile := &descriptorpb.FileDescriptorProto{
		Name:    proto.String("node.proto"),
		Syntax:  proto.String("proto3"),
		Package: proto.String("sample.tree"),
		MessageType: []*descriptorpb.DescriptorProto{
			{
				Name: proto.String("Node"),
				Field: []*descriptorpb.FieldDescriptorProto{
					{
						Name:     proto.String("val"),
						Number:   proto.Int32(1),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
						JsonName: proto.String("val"),
					},
					{
						Name:     proto.String("next"),
						Number:   proto.Int32(2),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
						TypeName: proto.String(".sample.tree.Node"),
						JsonName: proto.String("next"),
					},
				},
			},
		},
	}

	// 4. Any container schema
	anyContainerFile := &descriptorpb.FileDescriptorProto{
		Name:    proto.String("container.proto"),
		Syntax:  proto.String("proto3"),
		Package: proto.String("sample.any"),
		Dependency: []string{
			"google/protobuf/any.proto",
		},
		MessageType: []*descriptorpb.DescriptorProto{
			{
				Name: proto.String("SpecialEvent"),
				Field: []*descriptorpb.FieldDescriptorProto{
					{
						Name:     proto.String("event_id"),
						Number:   proto.Int32(1),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
						JsonName: proto.String("eventId"),
					},
					{
						Name:     proto.String("severity"),
						Number:   proto.Int32(2),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
						JsonName: proto.String("severity"),
					},
				},
			},
			{
				Name: proto.String("Envelope"),
				Field: []*descriptorpb.FieldDescriptorProto{
					{
						Name:     proto.String("trace_id"),
						Number:   proto.Int32(1),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
						JsonName: proto.String("traceId"),
					},
					{
						Name:     proto.String("payload"),
						Number:   proto.Int32(2),
						Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
						Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
						TypeName: proto.String(".google.protobuf.Any"),
						JsonName: proto.String("payload"),
					},
				},
			},
		},
	}

	anyFileDesc := anypb.File_google_protobuf_any_proto
	anyProto := protodesc.ToFileDescriptorProto(anyFileDesc)

	files, err := protodesc.NewFiles(&descriptorpb.FileDescriptorSet{
		File: []*descriptorpb.FileDescriptorProto{
			anyProto,
			proto3File,
			proto2File,
			recursiveFile,
			anyContainerFile,
		},
	})
	if err != nil {
		t.Fatalf("failed to build proto files: %v", err)
	}

	return files
}

func TestUnmarshalOptionsDiscardUnknown(t *testing.T) {
	files := buildDescriptors(t)
	desc, err := files.FindDescriptorByName("sample.user.User")
	if err != nil {
		t.Fatalf("FindDescriptorByName User failed: %v", err)
	}
	userDesc := desc.(protoreflect.MessageDescriptor)

	rawJSON := []byte(`{
		"userId": "9001",
		"name": "Alex",
		"unknownFieldOne": "extra",
		"nestedUnknown": {"foo": "bar"},
		"tags": ["alpha", "beta"]
	}`)

	// Default UnmarshalOptions (DiscardUnknown: false) must fail on unknown fields
	defaultOpts := protojson.UnmarshalOptions{}
	msgDefault := dynamicpb.NewMessage(userDesc)
	if err := defaultOpts.Unmarshal(rawJSON, msgDefault); err == nil {
		t.Fatalf("expected default UnmarshalOptions to return error on unknown fields, got nil")
	} else if !strings.Contains(err.Error(), "unknown") {
		t.Fatalf("expected error message to mention unknown field, got: %v", err)
	}

	// DiscardUnknown: true must succeed and populate known fields
	discardOpts := protojson.UnmarshalOptions{
		DiscardUnknown: true,
	}
	msgDiscard := dynamicpb.NewMessage(userDesc)
	if err := discardOpts.Unmarshal(rawJSON, msgDiscard); err != nil {
		t.Fatalf("Unmarshal with DiscardUnknown=true failed: %v", err)
	}

	if msgDiscard.Get(userDesc.Fields().ByName("user_id")).Int() != 9001 {
		t.Fatalf("expected user_id 9001, got %v", msgDiscard.Get(userDesc.Fields().ByName("user_id")).Int())
	}
	if msgDiscard.Get(userDesc.Fields().ByName("name")).String() != "Alex" {
		t.Fatalf("expected name 'Alex', got %s", msgDiscard.Get(userDesc.Fields().ByName("name")).String())
	}
	tagsList := msgDiscard.Get(userDesc.Fields().ByName("tags")).List()
	if tagsList.Len() != 2 || tagsList.Get(0).String() != "alpha" || tagsList.Get(1).String() != "beta" {
		t.Fatalf("tags list not populated correctly: %v", tagsList)
	}
}

func TestUnmarshalOptionsAllowPartial(t *testing.T) {
	files := buildDescriptors(t)
	desc, err := files.FindDescriptorByName("sample.item.RequiredItem")
	if err != nil {
		t.Fatalf("FindDescriptorByName RequiredItem failed: %v", err)
	}
	itemDesc := desc.(protoreflect.MessageDescriptor)

	// Incomplete JSON missing required field "id"
	partialJSON := []byte(`{
		"title": "Widget Alpha",
		"description": "A partially specified item"
	}`)

	// Default AllowPartial: false must fail due to uninitialized required field
	strictOpts := protojson.UnmarshalOptions{
		AllowPartial: false,
	}
	msgStrict := dynamicpb.NewMessage(itemDesc)
	if err := strictOpts.Unmarshal(partialJSON, msgStrict); err == nil {
		t.Fatalf("expected AllowPartial=false to fail on incomplete message missing required fields")
	}

	// AllowPartial: true must succeed and populate supplied fields
	partialOpts := protojson.UnmarshalOptions{
		AllowPartial: true,
	}
	msgPartial := dynamicpb.NewMessage(itemDesc)
	if err := partialOpts.Unmarshal(partialJSON, msgPartial); err != nil {
		t.Fatalf("Unmarshal with AllowPartial=true failed: %v", err)
	}

	if msgPartial.Get(itemDesc.Fields().ByName("title")).String() != "Widget Alpha" {
		t.Fatalf("expected title 'Widget Alpha', got %s", msgPartial.Get(itemDesc.Fields().ByName("title")).String())
	}
	if msgPartial.Get(itemDesc.Fields().ByName("description")).String() != "A partially specified item" {
		t.Fatalf("expected description 'A partially specified item', got %s", msgPartial.Get(itemDesc.Fields().ByName("description")).String())
	}
	if msgPartial.ProtoReflect().Has(itemDesc.Fields().ByName("id")) {
		t.Fatalf("expected missing required field 'id' to not have presence")
	}
}

func TestUnmarshalOptionsRecursionLimit(t *testing.T) {
	files := buildDescriptors(t)
	desc, err := files.FindDescriptorByName("sample.tree.Node")
	if err != nil {
		t.Fatalf("FindDescriptorByName Node failed: %v", err)
	}
	nodeDesc := desc.(protoreflect.MessageDescriptor)

	// JSON nested 4 levels deep
	deepJSON := []byte(`{
		"val": 1,
		"next": {
			"val": 2,
			"next": {
				"val": 3,
				"next": {
					"val": 4
				}
			}
		}
	}`)

	// RecursionLimit: 2 should fail because nesting exceeds depth
	shallowOpts := protojson.UnmarshalOptions{
		RecursionLimit: 2,
	}
	msgShallow := dynamicpb.NewMessage(nodeDesc)
	if err := shallowOpts.Unmarshal(deepJSON, msgShallow); err == nil {
		t.Fatalf("expected RecursionLimit=2 to return an error on deeply nested JSON, got nil")
	} else if !strings.Contains(err.Error(), "exceeded") && !strings.Contains(err.Error(), "recursion") && !strings.Contains(err.Error(), "depth") {
		t.Fatalf("expected recursion limit error message, got: %v", err)
	}

	// RecursionLimit: 10 should succeed
	deepOpts := protojson.UnmarshalOptions{
		RecursionLimit: 10,
	}
	msgDeep := dynamicpb.NewMessage(nodeDesc)
	if err := deepOpts.Unmarshal(deepJSON, msgDeep); err != nil {
		t.Fatalf("Unmarshal with RecursionLimit=10 failed: %v", err)
	}

	if msgDeep.Get(nodeDesc.Fields().ByName("val")).Int() != 1 {
		t.Fatalf("expected root val 1, got %v", msgDeep.Get(nodeDesc.Fields().ByName("val")).Int())
	}
	lvl2 := msgDeep.Get(nodeDesc.Fields().ByName("next")).Message()
	if lvl2.Get(nodeDesc.Fields().ByName("val")).Int() != 2 {
		t.Fatalf("expected lvl2 val 2, got %v", lvl2.Get(nodeDesc.Fields().ByName("val")).Int())
	}
}

func TestUnmarshalOptionsCustomResolver(t *testing.T) {
	files := buildDescriptors(t)
	envelopeDescRaw, err := files.FindDescriptorByName("sample.any.Envelope")
	if err != nil {
		t.Fatalf("FindDescriptorByName Envelope failed: %v", err)
	}
	envelopeDesc := envelopeDescRaw.(protoreflect.MessageDescriptor)

	eventDescRaw, err := files.FindDescriptorByName("sample.any.SpecialEvent")
	if err != nil {
		t.Fatalf("FindDescriptorByName SpecialEvent failed: %v", err)
	}
	eventDesc := eventDescRaw.(protoreflect.MessageDescriptor)

	anyJSON := []byte(`{
		"traceId": "trace-987",
		"payload": {
			"@type": "type.googleapis.com/sample.any.SpecialEvent",
			"eventId": "evt-001",
			"severity": 3
		}
	}`)

	// With an empty resolver that lacks SpecialEvent, unmarshaling the Any message must fail
	emptyRegistry := new(protoregistry.Types)
	emptyOpts := protojson.UnmarshalOptions{
		Resolver: emptyRegistry,
	}
	msgEmpty := dynamicpb.NewMessage(envelopeDesc)
	if err := emptyOpts.Unmarshal(anyJSON, msgEmpty); err == nil {
		t.Fatalf("expected empty resolver to fail resolving SpecialEvent Any payload")
	}

	// With a custom resolver containing SpecialEvent dynamic message type, unmarshaling must succeed
	customRegistry := new(protoregistry.Types)
	if err := customRegistry.RegisterMessage(dynamicpb.NewMessageType(eventDesc)); err != nil {
		t.Fatalf("RegisterMessage failed: %v", err)
	}

	customOpts := protojson.UnmarshalOptions{
		Resolver: customRegistry,
	}
	msgCustom := dynamicpb.NewMessage(envelopeDesc)
	if err := customOpts.Unmarshal(anyJSON, msgCustom); err != nil {
		t.Fatalf("Unmarshal with custom Resolver failed: %v", err)
	}

	if msgCustom.Get(envelopeDesc.Fields().ByName("trace_id")).String() != "trace-987" {
		t.Fatalf("expected traceId 'trace-987', got %s", msgCustom.Get(envelopeDesc.Fields().ByName("trace_id")).String())
	}

	// Verify Any payload unpacked contents
	payloadField := envelopeDesc.Fields().ByName("payload")
	anyMsgVal := msgCustom.Get(payloadField).Message().Interface()
	anyObj, ok := anyMsgVal.(*anypb.Any)
	if !ok {
		// Or dynamicpb message wrapping Any
		anyProtoBytes, err := proto.Marshal(msgCustom.Get(payloadField).Message().Interface())
		if err != nil {
			t.Fatalf("failed to marshal any field: %v", err)
		}
		anyObj = new(anypb.Any)
		if err := proto.Unmarshal(anyProtoBytes, anyObj); err != nil {
			t.Fatalf("failed to unmarshal into anypb.Any: %v", err)
		}
	}

	unpackedMsg := dynamicpb.NewMessage(eventDesc)
	if err := anypb.UnmarshalTo(anyObj, unpackedMsg, proto.UnmarshalOptions{Resolver: customRegistry}); err != nil {
		t.Fatalf("anypb.UnmarshalTo failed: %v", err)
	}

	if unpackedMsg.Get(eventDesc.Fields().ByName("event_id")).String() != "evt-001" {
		t.Fatalf("expected eventId 'evt-001', got %s", unpackedMsg.Get(eventDesc.Fields().ByName("event_id")).String())
	}
	if unpackedMsg.Get(eventDesc.Fields().ByName("severity")).Int() != 3 {
		t.Fatalf("expected severity 3, got %v", unpackedMsg.Get(eventDesc.Fields().ByName("severity")).Int())
	}
}

オリジンシーダー

匿名