CodeSampleX

Ejemplo

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

Muestra verificada para golang google.golang.org/protobuf v1.36.10: protojson.UnmarshalOptions. El contrato se ejecutó en go 1.26 · linux debian/x64 · docker…

sha256:4ed731dbfeadc3854f9ec9af234116089b61bec3a0c90cdd1ae8b21e47dcf60e

Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido. Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas. MIT-0

Evidencia de ejecución

El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.

Base de evidencia
Contrato firmado aprobado
Recibos de verificación
1
Claves de firma que lo compilaron
1
Entorno declarado go linux 24 · ubuntu · glibc 2.39 x64 go go go

Entornos de las ejecuciones de verificación

Entorno Contrato Etapas Ejecución
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

Caso

HOW
Objetivo
verify protojson.UnmarshalOptions in pkg:golang/google.golang.org/protobuf@v1.36.10
Paquetes
Símbolos
  • protojson.UnmarshalOptions
Entorno
go
Creado
2026-09-02T00:47:34Z

Contrato

  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.

Archivos

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

Descargar el artefacto de código fuente (tar.gz)

Código fuente

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())
	}
}

Seeder de origen

anónimo