The dynamicpb package creates protocol buffer messages using runtime type information, enabling work with message types that are only known at runtime. Dynamic messages implement the same interfaces as generated messages and can be used with all standard proto package functions.
import (
"google.golang.org/protobuf/types/dynamicpb"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/proto"
)Create new dynamic messages from message descriptors.
// NewMessage creates a new message with the provided descriptor
func NewMessage(desc protoreflect.MessageDescriptor) *MessageUsage example:
import (
"google.golang.org/protobuf/types/dynamicpb"
"google.golang.org/protobuf/reflect/protoregistry"
)
// Look up a message descriptor
md, err := protoregistry.GlobalFiles.FindDescriptorByName("example.MyMessage")
if err != nil {
log.Fatal(err)
}
// Create a dynamic message
msg := dynamicpb.NewMessage(md.(protoreflect.MessageDescriptor))
// Use the message with standard proto operations
data, err := proto.Marshal(msg)The Message type represents a dynamically constructed protocol buffer message.
// A Message is a dynamically constructed protocol buffer message
//
// Message implements the google.golang.org/protobuf/proto.Message interface,
// and may be used with all standard proto package functions such as Marshal,
// Unmarshal, and so forth.
//
// Message also implements the protoreflect.Message interface. See the
// protoreflect package documentation for that interface for how to get and set
// fields and otherwise interact with the contents of a Message.
//
// Reflection API functions which construct messages, such as NewField, return
// new dynamic messages of the appropriate type. Functions which take messages,
// such as Set for a message-value field, will accept any message with a
// compatible type.
//
// Operations which modify a Message are not safe for concurrent use.
type Message struct {
// Has unexported fields.
}
// ProtoReflect implements the protoreflect.ProtoMessage interface
func (m *Message) ProtoReflect() protoreflect.Message
// ProtoMessage implements the legacy message interface
func (m *Message) ProtoMessage()
// Reset clears the message to be empty, but preserves the dynamic message type
func (m *Message) Reset()
// String returns a string representation of a message
func (m *Message) String() string
// Descriptor returns the message descriptor
func (m *Message) Descriptor() protoreflect.MessageDescriptor
// Type returns the message type
func (m *Message) Type() protoreflect.MessageType
// New returns a newly allocated empty message with the same descriptor
// See protoreflect.Message for details
func (m *Message) New() protoreflect.Message
// Interface returns the message
// See protoreflect.Message for details
func (m *Message) Interface() protoreflect.ProtoMessage
// IsValid reports whether the message is valid
// See protoreflect.Message for details
func (m *Message) IsValid() boolReflective field manipulation for dynamic messages.
// Range visits every populated field in undefined order
// See protoreflect.Message for details
func (m *Message) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool)
// Has reports whether a field is populated
// See protoreflect.Message for details
func (m *Message) Has(fd protoreflect.FieldDescriptor) bool
// Get returns the value of a field
// See protoreflect.Message for details
func (m *Message) Get(fd protoreflect.FieldDescriptor) protoreflect.Value
// Set stores a value in a field
// See protoreflect.Message for details
func (m *Message) Set(fd protoreflect.FieldDescriptor, v protoreflect.Value)
// Clear clears a field
// See protoreflect.Message for details
func (m *Message) Clear(fd protoreflect.FieldDescriptor)
// Mutable returns a mutable reference to a repeated, map, or message field
// See protoreflect.Message for details
func (m *Message) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value
// NewField returns a new value for assignable to the field of a given descriptor
// See protoreflect.Message for details
func (m *Message) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value
// WhichOneof reports which field in a oneof is populated, returning nil if none are populated
// See protoreflect.Message for details
func (m *Message) WhichOneof(od protoreflect.OneofDescriptor) protoreflect.FieldDescriptorManage unknown fields in dynamic messages.
// GetUnknown returns the raw unknown fields
// See protoreflect.Message for details
func (m *Message) GetUnknown() protoreflect.RawFields
// SetUnknown sets the raw unknown fields
// See protoreflect.Message for details
func (m *Message) SetUnknown(r protoreflect.RawFields)// ProtoMethods is an internal detail of the protoreflect.Message interface
// Users should never call this directly
func (m *Message) ProtoMethods() *protoiface.MethodsUsage example:
// Create a dynamic message
msg := dynamicpb.NewMessage(messageDescriptor)
// Get field descriptor
fields := messageDescriptor.Fields()
nameField := fields.ByName("name")
valueField := fields.ByName("value")
// Set field values
msg.Set(nameField, protoreflect.ValueOfString("example"))
msg.Set(valueField, protoreflect.ValueOfInt32(42))
// Check if field is set
if msg.Has(nameField) {
name := msg.Get(nameField).String()
fmt.Println("Name:", name)
}
// Iterate over all fields
msg.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
fmt.Printf("Field %s: %v\n", fd.Name(), v)
return true
})
// Marshal to wire format
data, err := proto.Marshal(msg)Create message types from message descriptors.
// NewMessageType creates a new MessageType with the provided descriptor
//
// MessageTypes created by this package are equal if their descriptors
// are equal. That is, if md1 == md2, then NewMessageType(md1) ==
// NewMessageType(md2).
func NewMessageType(desc protoreflect.MessageDescriptor) protoreflect.MessageTypeUsage example:
md, _ := protoregistry.GlobalFiles.FindDescriptorByName("example.MyMessage")
mt := dynamicpb.NewMessageType(md.(protoreflect.MessageDescriptor))
// Create instances
msg := mt.New().Interface()Create enum types from enum descriptors.
// NewEnumType creates a new EnumType with the provided descriptor
//
// EnumTypes created by this package are equal if their descriptors are equal.
// That is, if ed1 == ed2, then NewEnumType(ed1) == NewEnumType(ed2).
//
// Enum values created by the EnumType are equal if their numbers are equal.
func NewEnumType(desc protoreflect.EnumDescriptor) protoreflect.EnumTypeCreate extension types from extension descriptors.
// NewExtensionType creates a new ExtensionType with the provided descriptor
//
// Dynamic ExtensionTypes with the same descriptor compare as equal. That is,
// if xd1 == xd2, then NewExtensionType(xd1) == NewExtensionType(xd2).
//
// The InterfaceOf and ValueOf methods of the extension type are defined as:
//
// func (xt extensionType) ValueOf(iv any) protoreflect.Value {
// return protoreflect.ValueOf(iv)
// }
//
// func (xt extensionType) InterfaceOf(v protoreflect.Value) any {
// return v.Interface()
// }
//
// The Go type used by the proto.GetExtension and proto.SetExtension functions
// is determined by these methods, and is therefore equivalent to the Go
// type used to represent a protoreflect.Value. See the protoreflect.Value
// documentation for more details.
func NewExtensionType(desc protoreflect.ExtensionDescriptor) protoreflect.ExtensionTypeA collection of dynamically constructed descriptors with concurrent-safe lookup methods.
// A Types is a collection of dynamically constructed descriptors
// Its methods are safe for concurrent use
//
// Types implements protoregistry.MessageTypeResolver and
// protoregistry.ExtensionTypeResolver. A Types may be used as a
// google.golang.org/protobuf/proto.UnmarshalOptions.Resolver.
type Types struct {
// Has unexported fields.
}
// NewTypes creates a new Types registry with the provided files
// The Files registry is retained, and changes to Files will be reflected in Types
// It is not safe to concurrently change the Files while calling Types methods
func NewTypes(f *protoregistry.Files) *TypesFind message types in the dynamic registry.
// FindMessageByName looks up a message by its full name
// e.g. "google.protobuf.Any"
//
// This returns (nil, protoregistry.NotFound) if not found
func (t *Types) FindMessageByName(name protoreflect.FullName) (protoreflect.MessageType, error)
// FindMessageByURL looks up a message by a URL identifier
// See documentation on google.protobuf.Any.type_url for the URL format
//
// This returns (nil, protoregistry.NotFound) if not found
func (t *Types) FindMessageByURL(url string) (protoreflect.MessageType, error)Find enum types in the dynamic registry.
// FindEnumByName looks up an enum by its full name
// e.g., "google.protobuf.Field.Kind"
//
// This returns (nil, protoregistry.NotFound) if not found
func (t *Types) FindEnumByName(name protoreflect.FullName) (protoreflect.EnumType, error)Find extension types in the dynamic registry.
// FindExtensionByName looks up an extension field by the field's full name
// Note that this is the full name of the field as determined by where the
// extension is declared and is unrelated to the full name of the message being
// extended
//
// This returns (nil, protoregistry.NotFound) if not found
func (t *Types) FindExtensionByName(name protoreflect.FullName) (protoreflect.ExtensionType, error)
// FindExtensionByNumber looks up an extension field by the field number within
// some parent message, identified by full name
//
// This returns (nil, protoregistry.NotFound) if not found
func (t *Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)Usage example:
import (
"google.golang.org/protobuf/types/dynamicpb"
"google.golang.org/protobuf/reflect/protoregistry"
)
// Create a dynamic types registry
files := protoregistry.GlobalFiles
types := dynamicpb.NewTypes(files)
// Look up a message type
mt, err := types.FindMessageByName("google.protobuf.Any")
if err != nil {
log.Fatal(err)
}
// Create a new instance
msg := mt.New()
// Use as UnmarshalOptions.Resolver
opts := proto.UnmarshalOptions{
Resolver: types,
}
err = opts.Unmarshal(data, msg.Interface())// Load file descriptors from a FileDescriptorSet
fds := &descriptorpb.FileDescriptorSet{}
err := proto.Unmarshal(descriptorData, fds)
files, err := protodesc.NewFiles(fds)
types := dynamicpb.NewTypes(files)
// Create message from type URL
mt, err := types.FindMessageByURL("type.googleapis.com/example.MyMessage")
msg := mt.New().Interface()
// Unmarshal data into dynamic message
err = proto.Unmarshal(wireData, msg)
// Access fields dynamically
md := msg.ProtoReflect().Descriptor()
field := md.Fields().ByName("my_field")
value := msg.ProtoReflect().Get(field)// Get descriptor for a message type
md, _ := protoregistry.GlobalFiles.FindDescriptorByName("example.MyMessage")
msgDesc := md.(protoreflect.MessageDescriptor)
// Create dynamic message
msg := dynamicpb.NewMessage(msgDesc)
// Build message programmatically
fields := msgDesc.Fields()
for i := 0; i < fields.Len(); i++ {
fd := fields.Get(i)
// Set default values or read from configuration
switch fd.Kind() {
case protoreflect.StringKind:
msg.Set(fd, protoreflect.ValueOfString("default"))
case protoreflect.Int32Kind:
msg.Set(fd, protoreflect.ValueOfInt32(0))
}
}This documentation covers the complete API for working with dynamic protocol buffer messages at runtime, including message creation, field manipulation, type construction, and dynamic type registries.