The reflection packages provide powerful runtime introspection and manipulation capabilities for protocol buffer messages. These packages enable working with messages dynamically without compile-time knowledge of their types, building descriptor hierarchies, traversing message structures, and maintaining type registries.
import (
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protopath"
"google.golang.org/protobuf/reflect/protorange"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/descriptorpb"
)Create file descriptors from FileDescriptorProto messages with dependency resolution.
// NewFile creates a new protoreflect.FileDescriptor from the provided file
// descriptor message. See FileOptions.New for more information.
func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error)
// NewFiles creates a new protoregistry.Files from the provided
// FileDescriptorSet message. See FileOptions.NewFiles for more information.
func NewFiles(fd *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error)Configure file descriptor construction with control over dependency resolution.
// FileOptions configures the construction of file descriptors
type FileOptions struct {
pragma.NoUnkeyedLiterals
// AllowUnresolvable configures New to permissively allow unresolvable
// file, enum, or message dependencies. Unresolved dependencies are replaced
// by placeholder equivalents.
//
// The following dependencies may be left unresolved:
// • Resolving an imported file.
// • Resolving the type for a message field or extension field.
// If the kind of the field is unknown, then a placeholder is used for both
// the Enum and Message accessors on the protoreflect.FieldDescriptor.
// • Resolving an enum value set as the default for an optional enum field.
// If unresolvable, the protoreflect.FieldDescriptor.Default is set to the
// first value in the associated enum (or zero if the also enum dependency
// is also unresolvable). The protoreflect.FieldDescriptor.DefaultEnumValue
// is populated with a placeholder.
// • Resolving the extended message type for an extension field.
// • Resolving the input or output message type for a service method.
//
// If the unresolved dependency uses a relative name,
// then the placeholder will contain an invalid FullName with a "*." prefix,
// indicating that the starting prefix of the full name is unknown.
AllowUnresolvable bool
}
// New creates a new protoreflect.FileDescriptor from the provided file
// descriptor message. The file must represent a valid proto file according to
// protobuf semantics. The returned descriptor is a deep copy of the input.
//
// Any imported files, enum types, or message types referenced in the file are
// resolved using the provided registry. When looking up an import file path,
// the path must be unique. The newly created file descriptor is not registered
// back into the provided file registry.
func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error)
// NewFiles creates a new protoregistry.Files from the provided
// FileDescriptorSet message. The descriptor set must include only valid files
// according to protobuf semantics. The returned descriptors are a deep copy of
// the input.
func (o FileOptions) NewFiles(fds *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error)Interface for resolving dependencies during descriptor construction.
// Resolver is the resolver used by NewFile to resolve dependencies
// The enums and messages provided must belong to some parent file, which is also
// registered.
//
// It is implemented by protoregistry.Files.
type Resolver interface {
FindFileByPath(string) (protoreflect.FileDescriptor, error)
FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
}Convert reflection descriptors back to proto message representations.
// ToFileDescriptorProto copies a protoreflect.FileDescriptor into a
// google.protobuf.FileDescriptorProto message
func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileDescriptorProto
// ToDescriptorProto copies a protoreflect.MessageDescriptor into a
// google.protobuf.DescriptorProto message
func ToDescriptorProto(message protoreflect.MessageDescriptor) *descriptorpb.DescriptorProto
// ToFieldDescriptorProto copies a protoreflect.FieldDescriptor into a
// google.protobuf.FieldDescriptorProto message
func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.FieldDescriptorProto
// ToOneofDescriptorProto copies a protoreflect.OneofDescriptor into a
// google.protobuf.OneofDescriptorProto message
func ToOneofDescriptorProto(oneof protoreflect.OneofDescriptor) *descriptorpb.OneofDescriptorProto
// ToEnumDescriptorProto copies a protoreflect.EnumDescriptor into a
// google.protobuf.EnumDescriptorProto message
func ToEnumDescriptorProto(enum protoreflect.EnumDescriptor) *descriptorpb.EnumDescriptorProto
// ToEnumValueDescriptorProto copies a protoreflect.EnumValueDescriptor into a
// google.protobuf.EnumValueDescriptorProto message
func ToEnumValueDescriptorProto(value protoreflect.EnumValueDescriptor) *descriptorpb.EnumValueDescriptorProto
// ToServiceDescriptorProto copies a protoreflect.ServiceDescriptor into a
// google.protobuf.ServiceDescriptorProto message
func ToServiceDescriptorProto(service protoreflect.ServiceDescriptor) *descriptorpb.ServiceDescriptorProto
// ToMethodDescriptorProto copies a protoreflect.MethodDescriptor into a
// google.protobuf.MethodDescriptorProto message
func ToMethodDescriptorProto(method protoreflect.MethodDescriptor) *descriptorpb.MethodDescriptorProtoRepresent sequences of reflection operations as structured paths through message hierarchies.
// Path is a sequence of protobuf reflection steps applied to some root
// protobuf message value to arrive at the current value
// The first step must be a Root step
type Path []Step
// Index returns the ith step in the path and supports negative indexing
// A negative index starts counting from the tail of the Path such that -1
// refers to the last step, -2 refers to the second-to-last step, and so on
// It returns a zero Step value if the index is out-of-bounds
func (p Path) Index(i int) Step
// String returns a structured representation of the path by concatenating the
// string representation of every path step
func (p Path) String() stringDefine individual steps in a path through a message structure.
// Step is a union where only one step operation may be specified at a time
// The different kinds of steps are specified by the constants defined for the
// StepKind type
type Step struct {
// Has unexported fields.
}
// Root indicates the root message that a path is relative to
// It should always (and only ever) be the first step in a path
func Root(md protoreflect.MessageDescriptor) Step
// FieldAccess describes access of a field within a message
// Extension field accesses are also represented using a FieldAccess and must be
// provided with a protoreflect.FieldDescriptor
func FieldAccess(fd protoreflect.FieldDescriptor) Step
// UnknownAccess describes access to the unknown fields within a message
func UnknownAccess() Step
// ListIndex describes index of an element within a list
func ListIndex(i int) Step
// MapIndex describes index of an entry within a map
// The key type is determined by field descriptor that the map belongs to
func MapIndex(k protoreflect.MapKey) Step
// AnyExpand describes expansion of a google.protobuf.Any message into a
// structured representation of the underlying message
func AnyExpand(md protoreflect.MessageDescriptor) Step
// Kind reports which kind of step this is
func (s Step) Kind() StepKind
// MessageDescriptor returns the message descriptor for Root or AnyExpand
// steps, otherwise it returns nil
func (s Step) MessageDescriptor() protoreflect.MessageDescriptor
// FieldDescriptor returns the field descriptor for FieldAccess steps,
// otherwise it returns nil
func (s Step) FieldDescriptor() protoreflect.FieldDescriptor
// ListIndex returns the list index for ListIndex steps, otherwise it returns 0
func (s Step) ListIndex() int
// MapIndex returns the map key for MapIndex steps, otherwise it returns an
// invalid map key
func (s Step) MapIndex() protoreflect.MapKey
// String returns a string representation of the step
func (s Step) String() string// StepKind identifies the kind of step operation
// Each kind of step corresponds with some protobuf reflection operation
type StepKind int
const (
// RootStep identifies a step as the Root step operation
RootStep StepKind
// FieldAccessStep identifies a step as the FieldAccess step operation
FieldAccessStep
// UnknownAccessStep identifies a step as the UnknownAccess step operation
UnknownAccessStep
// ListIndexStep identifies a step as the ListIndex step operation
ListIndexStep
// MapIndexStep identifies a step as the MapIndex step operation
MapIndexStep
// AnyExpandStep identifies a step as the AnyExpand step operation
AnyExpandStep
)
func (k StepKind) String() stringPair paths with the values at each step for complete traversal state.
// Values is a Path paired with a sequence of values at each step
// The lengths of Values.Path and Values.Values must be identical
// The first step must be a Root step and the first value must be a concrete message value
type Values struct {
Path Path
Values []protoreflect.Value
}
// Len reports the length of the path and values
// If the path and values have differing length, it returns the minimum length
func (p Values) Len() int
// Index returns the ith step and value and supports negative indexing
// A negative index starts counting from the tail of the Values such that -1
// refers to the last pair, -2 refers to the second-to-last pair, and so on
func (p Values) Index(i int) (out struct {
Step Step
Value protoreflect.Value
})
// String returns a humanly readable representation of the path and last value
// Do not depend on the output being stable
func (p Values) String() stringPerform depth-first traversal over message structures with callback-based navigation.
// Range performs a depth-first traversal over reachable values in a message
// See Options.Range for details
func Range(m protoreflect.Message, f func(protopath.Values) error) errorConstants for controlling traversal behavior.
var (
// Break breaks traversal of children in the current value
// It has no effect when traversing values that are not composite types
// (e.g., messages, lists, and maps)
Break = errors.New("break traversal of children in current value")
// Terminate terminates the entire range operation
// All necessary Pop operations continue to be called
Terminate = errors.New("terminate range operation")
)Configure message traversal behavior including ordering and type resolution.
// Options configures traversal of a message value tree
type Options struct {
// Stable specifies whether to visit message fields and map entries
// in a stable ordering. If false, then the ordering is undefined and
// may be non-deterministic.
//
// Message fields are visited in ascending order by field number.
// Map entries are visited in ascending order, where
// boolean keys are ordered such that false sorts before true,
// numeric keys are ordered based on the numeric value, and
// string keys are lexicographically ordered by Unicode codepoints.
Stable bool
// Resolver is used for looking up types when expanding google.protobuf.Any
// messages. If nil, this defaults to using protoregistry.GlobalTypes.
// To prevent expansion of Any messages, pass an empty protoregistry.Types:
//
// Options{Resolver: (*protoregistry.Types)(nil)}
//
Resolver interface {
protoregistry.ExtensionTypeResolver
protoregistry.MessageTypeResolver
}
}
// Range performs a depth-first traversal over reachable values in a message
// The first push and the last pop are to push/pop a protopath.Root step
// If push or pop return any non-nil error (other than Break or Terminate),
// it terminates the traversal and is returned by Range
//
// The rules for traversing a message is as follows:
//
// - For messages, iterate over every populated known and extension field.
// Each field is preceded by a push of a protopath.FieldAccess step,
// followed by recursive application of the rules on the field value,
// and succeeded by a pop of that step. If the message has unknown fields,
// then push an protopath.UnknownAccess step followed immediately by pop of
// that step.
//
// - As an exception to the above rule, if the current message is a
// google.protobuf.Any message, expand the underlying message (if
// resolvable). The expanded message is preceded by a push of a
// protopath.AnyExpand step, followed by recursive application of the
// rules on the underlying message, and succeeded by a pop of that step.
// Mutations to the expanded message are written back to the Any message
// when popping back out.
//
// - For lists, iterate over every element. Each element is preceded by a
// push of a protopath.ListIndex step, followed by recursive application of
// the rules on the list element, and succeeded by a pop of that step.
//
// - For maps, iterate over every entry. Each entry is preceded by a push
// of a protopath.MapIndex step, followed by recursive application of the
// rules on the map entry value, and succeeded by a pop of that step.
//
// Mutations should only be made to the last value, otherwise the effects
// on traversal will be undefined. If the mutation is made to the last value
// during to a push, then the effects of the mutation will affect traversal.
//
// The protopath.Values provided to push functions is only valid until the
// corresponding pop call and the values provided to a pop call is only valid
// for the duration of the pop call itself.
func (o Options) Range(m protoreflect.Message, push, pop func(protopath.Values) error) errorUsage example:
import (
"fmt"
"google.golang.org/protobuf/reflect/protorange"
"google.golang.org/protobuf/reflect/protopath"
)
// Simple traversal
err := protorange.Range(msg.ProtoReflect(), func(p protopath.Values) error {
fmt.Printf("Path: %v, Value: %v\n", p.Path, p.Index(-1).Value)
return nil
})
// Traversal with push/pop callbacks
opts := protorange.Options{Stable: true}
err = opts.Range(msg.ProtoReflect(),
func(p protopath.Values) error {
// Called before visiting
fmt.Printf("Entering: %v\n", p.Path)
return nil
},
func(p protopath.Values) error {
// Called after visiting
fmt.Printf("Leaving: %v\n", p.Path)
return nil
},
)Access the global registry for message types, extension types, and enum types.
// GlobalTypes is a global registry of types
var GlobalTypes *Types
// GlobalFiles is a global registry of files
var GlobalFiles *FilesRegistry for looking up and registering file descriptors.
// Files is a registry for looking up file descriptors
type Files struct {
// Has unexported fields.
}
// NewFiles creates a new file registry
func NewFiles(files ...protoreflect.FileDescriptor) (*Files, error)
// RegisterFile registers the provided file descriptor
// If any descriptor within the file conflicts with the descriptor already
// registered in files, then files is unchanged and an error is returned
func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error
// FindFileByPath looks up a file by the path
// The path must be a proto import path
func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error)
// FindDescriptorByName looks up a descriptor by its full name
func (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error)
// NumFiles reports the number of registered files
func (r *Files) NumFiles() int
// RangeFiles iterates over all registered files
// The iteration order is undefined
func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool)
// NumFilesByPackage reports the number of registered files in a proto package
func (r *Files) NumFilesByPackage(name protoreflect.FullName) int
// RangeFilesByPackage iterates over all registered files in a proto package
// The iteration order is undefined
func (r *Files) RangeFilesByPackage(name protoreflect.FullName, f func(protoreflect.FileDescriptor) bool)Registry for looking up and registering message types, extension types, and enum types.
// Types is a registry for looking up types
type Types struct {
// Has unexported fields.
}
// NewTypes creates a new type registry
func NewTypes(typs ...interface{}) (*Types, error)
// RegisterMessage registers the provided message type
func (r *Types) RegisterMessage(mt protoreflect.MessageType) error
// FindMessageByName looks up a message by its full name
func (r *Types) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error)
// FindMessageByURL looks up a message by a URL identifier
// The URL is of the form "type.googleapis.com/package.Message"
func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error)
// RegisterEnum registers the provided enum type
func (r *Types) RegisterEnum(et protoreflect.EnumType) error
// FindEnumByName looks up an enum by its full name
func (r *Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumType, error)
// RegisterExtension registers the provided extension type
func (r *Types) RegisterExtension(xt protoreflect.ExtensionType) error
// FindExtensionByName looks up an extension by its full name
func (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
// FindExtensionByNumber looks up an extension by field number within some parent message
func (r *Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
// NumMessages reports the number of registered messages
func (r *Types) NumMessages() int
// RangeMessages iterates over all registered messages
// The iteration order is undefined
func (r *Types) RangeMessages(f func(protoreflect.MessageType) bool)
// NumEnums reports the number of registered enums
func (r *Types) NumEnums() int
// RangeEnums iterates over all registered enums
// The iteration order is undefined
func (r *Types) RangeEnums(f func(protoreflect.EnumType) bool)
// NumExtensions reports the number of registered extensions
func (r *Types) NumExtensions() int
// RangeExtensions iterates over all registered extensions
// The iteration order is undefined
func (r *Types) RangeExtensions(f func(protoreflect.ExtensionType) bool)
// NumExtensionsByMessage reports the number of registered extensions for a given message
func (r *Types) NumExtensionsByMessage(message protoreflect.FullName) int
// RangeExtensionsByMessage iterates over all registered extensions for a given message
// The iteration order is undefined
func (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool)// NotFound is a sentinel error value to indicate that the type was not found
var NotFound = errors.New("not found")Usage example:
import (
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)
// Register a message type
registry := &protoregistry.Types{}
err := registry.RegisterMessage((&pb.MyMessage{}).ProtoReflect().Type())
// Look up a message type
mt, err := protoregistry.GlobalTypes.FindMessageByName("example.MyMessage")
if err != nil {
log.Fatal(err)
}
// Create a new instance
msg := mt.New().Interface()The primary interface for reflective message manipulation.
// Message is a reflective view over a concrete message instance
type Message interface {
// Descriptor returns the message descriptor
Descriptor() MessageDescriptor
// Type returns the message type which contains both Go and protobuf type information
Type() MessageType
// New returns a newly allocated and mutable empty message
New() Message
// Interface unwraps the message reflection interface and
// returns the underlying ProtoMessage interface
Interface() ProtoMessage
// Range iterates over every populated field in the message
// The iteration order is undefined
Range(func(FieldDescriptor, Value) bool)
// Has reports whether a field is populated
Has(FieldDescriptor) bool
// Clear clears the field such that a subsequent Has call reports false
Clear(FieldDescriptor)
// Get retrieves the value for a field
Get(FieldDescriptor) Value
// Set stores the value for a field
Set(FieldDescriptor, Value)
// Mutable returns a mutable reference to a composite type
Mutable(FieldDescriptor) Value
// NewField returns a new value that is assignable to the field for the given descriptor
NewField(FieldDescriptor) Value
// WhichOneof reports which field within the oneof is populated, returning nil if none are populated
WhichOneof(OneofDescriptor) FieldDescriptor
// GetUnknown retrieves the entire list of unknown fields
GetUnknown() RawFields
// SetUnknown stores an entire list of unknown fields
SetUnknown(RawFields)
// IsValid reports whether the message is valid
IsValid() bool
}The top-level interface that all generated messages implement.
// ProtoMessage is the top-level interface that all messages must implement
type ProtoMessage interface {
// ProtoReflect returns a reflective view of the message
ProtoReflect() Message
}Represents a value in the protobuf type system.
// Value is a union type that can represent any protobuf value
type Value value
// ValueOf returns a Value for a Go value
func ValueOf(v interface{}) Value
// ValueOfBool returns a new bool value
func ValueOfBool(v bool) Value
// ValueOfInt32 returns a new int32 value
func ValueOfInt32(v int32) Value
// ValueOfInt64 returns a new int64 value
func ValueOfInt64(v int64) Value
// ValueOfUint32 returns a new uint32 value
func ValueOfUint32(v uint32) Value
// ValueOfUint64 returns a new uint64 value
func ValueOfUint64(v uint64) Value
// ValueOfFloat32 returns a new float32 value
func ValueOfFloat32(v float32) Value
// ValueOfFloat64 returns a new float64 value
func ValueOfFloat64(v float64) Value
// ValueOfString returns a new string value
func ValueOfString(v string) Value
// ValueOfBytes returns a new bytes value
func ValueOfBytes(v []byte) Value
// ValueOfEnum returns a new enum value
func ValueOfEnum(v EnumNumber) Value
// ValueOfMessage returns a new message value
func ValueOfMessage(v Message) Value
// ValueOfList returns a new list value
func ValueOfList(v List) Value
// ValueOfMap returns a new map value
func ValueOfMap(v Map) Value
// IsValid reports whether v is populated with a value
func (v Value) IsValid() bool
// Interface returns v as an interface{}
func (v Value) Interface() interface{}
// Bool returns the value as a bool
func (v Value) Bool() bool
// Int returns the value as an int64
func (v Value) Int() int64
// Uint returns the value as a uint64
func (v Value) Uint() uint64
// Float returns the value as a float64
func (v Value) Float() float64
// String returns the value as a string
func (v Value) String() string
// Bytes returns the value as a []byte
func (v Value) Bytes() []byte
// Enum returns the value as an EnumNumber
func (v Value) Enum() EnumNumber
// Message returns the value as a Message
func (v Value) Message() Message
// List returns the value as a List
func (v Value) List() List
// Map returns the value as a Map
func (v Value) Map() Map
// MapKey returns the value as a MapKey
func (v Value) MapKey() MapKey
// Equal reports whether v and w are equal
func (v Value) Equal(w Value) boolThis comprehensive reflection documentation covers all major APIs for working with protocol buffers at runtime through reflection, including descriptor construction, path-based traversal, message introspection, and type registries.