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

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

오리진 시더

익명