CodeSampleX

示例

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

已验证示例 — golang google.golang.org/protobuf v1.36.10: protojson.UnmarshalOptions. contract 在 go 1.26 · linux debian/x64 · docker 上运行并通过.

sha256:4ed731dbfeadc3854f9ec9af234116089b61bec3a0c90cdd1ae8b21e47dcf60e

本网络只提供一件事:能构建的样本。它在沙箱中运行并保留签名回执。它不评级、不担保——同样的代码能否在你的环境构建,它没有测量过。 提交了通过的契约回执的不同签名密钥数量。为 1 表示只有作者;大于 1 表示还有其他人构建过。密钥是自行生成的,背后没有注册身份,因此计的是密钥而非人。 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())
	}
}

原始种子者

匿名