reflect/protoreflect: add FieldDescriptor.TextName

Add a new TextName accessor that returns the field name that should
be used for the text format. It is usually just the field name, except:
1) it uses the inlined message name for groups,
2) uses the full name surrounded by brackets for extensions, and
3) strips the "message_set_extension" for well-formed extensions
to the proto1 MessageSet.

We make similar adjustments to the JSONName accessor so that it applies
similar semantics for extensions.

The two changes simplifies all logic that wants the humanly readable
name for a field.

Change-Id: I524b6e017fb955146db81819270fe197f8f97980
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/239838
Reviewed-by: Herbie Ong <herbie@google.com>
This commit is contained in:
Joe Tsai 2020-06-24 16:47:32 -07:00
parent 467a9cdc10
commit e14d6b3cdc
21 changed files with 135 additions and 113 deletions

View File

@ -184,17 +184,7 @@ func (d decoder) unmarshalFields(m pref.Message, skipTypeURL bool) error {
// The name can either be the JSON name or the proto field name.
fd = fieldDescs.ByJSONName(name)
if fd == nil {
fd = fieldDescs.ByName(pref.Name(name))
if fd == nil {
// The proto name of a group field is in all lowercase,
// while the textual field name is the group message name.
gd := fieldDescs.ByName(pref.Name(strings.ToLower(name)))
if gd != nil && gd.Kind() == pref.GroupKind && gd.Message().Name() == pref.Name(name) {
fd = gd
}
} else if fd.Kind() == pref.GroupKind && fd.Message().Name() != pref.Name(name) {
fd = nil // reset since field name is actually the message name
}
fd = fieldDescs.ByTextName(name)
}
}
if flags.ProtoLegacy {

View File

@ -198,22 +198,9 @@ func (e encoder) marshalFields(m pref.Message) error {
var err error
order.RangeFields(fields, order.IndexNameFieldOrder, func(fd pref.FieldDescriptor, v pref.Value) bool {
var name string
switch {
case fd.IsExtension():
if messageset.IsMessageSetExtension(fd) {
name = "[" + string(fd.FullName().Parent()) + "]"
} else {
name = "[" + string(fd.FullName()) + "]"
}
case e.opts.UseProtoNames:
if fd.Kind() == pref.GroupKind {
name = string(fd.Message().Name())
} else {
name = string(fd.Name())
}
default:
name = fd.JSONName()
name := fd.JSONName()
if e.opts.UseProtoNames {
name = fd.TextName()
}
if err = e.WriteName(name); err != nil {

View File

@ -6,7 +6,6 @@ package prototext
import (
"fmt"
"strings"
"unicode/utf8"
"google.golang.org/protobuf/internal/encoding/messageset"
@ -158,17 +157,7 @@ func (d decoder) unmarshalMessage(m pref.Message, checkDelims bool) error {
switch tok.NameKind() {
case text.IdentName:
name = pref.Name(tok.IdentName())
fd = fieldDescs.ByName(name)
if fd == nil {
// The proto name of a group field is in all lowercase,
// while the textproto field name is the group message name.
gd := fieldDescs.ByName(pref.Name(strings.ToLower(string(name))))
if gd != nil && gd.Kind() == pref.GroupKind && gd.Message().Name() == name {
fd = gd
}
} else if fd.Kind() == pref.GroupKind && fd.Message().Name() != name {
fd = nil // reset since field name is actually the message name
}
fd = fieldDescs.ByTextName(string(name))
case text.TypeName:
// Handle extensions only. This code path is not for Any.

View File

@ -172,22 +172,7 @@ func (e encoder) marshalMessage(m pref.Message, inclDelims bool) error {
// Marshal fields.
var err error
order.RangeFields(m, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
var name string
if fd.IsExtension() {
if messageset.IsMessageSetExtension(fd) {
name = "[" + string(fd.FullName().Parent()) + "]"
} else {
name = "[" + string(fd.FullName()) + "]"
}
} else {
if fd.Kind() == pref.GroupKind {
name = string(fd.Message().Name())
} else {
name = string(fd.Name())
}
}
if err = e.marshalField(string(name), v, fd); err != nil {
if err = e.marshalField(fd.TextName(), v, fd); err != nil {
return false
}
return true

View File

@ -100,6 +100,7 @@ var descListTypesTemplate = template.Must(template.New("").Parse(`
byName map[protoreflect.Name]*{{$nameDesc}} // protected by once
{{- if (eq . "Field")}}
byJSON map[string]*{{$nameDesc}} // protected by once
byText map[string]*{{$nameDesc}} // protected by once
{{- end}}
{{- if .NumberExpr}}
byNum map[{{.NumberExpr}}]*{{$nameDesc}} // protected by once
@ -125,6 +126,12 @@ var descListTypesTemplate = template.Must(template.New("").Parse(`
}
return nil
}
func (p *{{$nameList}}) ByTextName(s string) {{.Expr}} {
if d := p.lazyInit().byText[s]; d != nil {
return d
}
return nil
}
{{- end}}
{{- if .NumberExpr}}
func (p *{{$nameList}}) ByNumber(n {{.NumberExpr}}) {{.Expr}} {
@ -144,6 +151,7 @@ var descListTypesTemplate = template.Must(template.New("").Parse(`
p.byName = make(map[protoreflect.Name]*{{$nameDesc}}, len(p.List))
{{- if (eq . "Field")}}
p.byJSON = make(map[string]*{{$nameDesc}}, len(p.List))
p.byText = make(map[string]*{{$nameDesc}}, len(p.List))
{{- end}}
{{- if .NumberExpr}}
p.byNum = make(map[{{.NumberExpr}}]*{{$nameDesc}}, len(p.List))
@ -157,6 +165,9 @@ var descListTypesTemplate = template.Must(template.New("").Parse(`
if _, ok := p.byJSON[d.JSONName()]; !ok {
p.byJSON[d.JSONName()] = d
}
if _, ok := p.byText[d.TextName()]; !ok {
p.byText[d.TextName()] = d
}
{{- end}}
{{- if .NumberExpr}}
if _, ok := p.byNum[d.Number()]; !ok {

View File

@ -22,6 +22,7 @@ func TestDescriptorAccessors(t *testing.T) {
"ProtoInternal": true,
"ProtoType": true,
"TextName": true, // derived from other fields
"HasOptionalKeyword": true, // captured by HasPresence
"IsSynthetic": true, // captured by HasPresence

View File

@ -104,7 +104,7 @@ func Unmarshal(tag string, goType reflect.Type, evs pref.EnumValueDescriptors) p
case strings.HasPrefix(s, "json="):
jsonName := s[len("json="):]
if jsonName != strs.JSONCamelCase(string(f.L0.FullName.Name())) {
f.L1.JSONName.Init(jsonName)
f.L1.StringName.InitJSON(jsonName)
}
case s == "packed":
f.L1.HasPacked = true

View File

@ -13,6 +13,7 @@ import (
"google.golang.org/protobuf/internal/descfmt"
"google.golang.org/protobuf/internal/descopts"
"google.golang.org/protobuf/internal/encoding/defval"
"google.golang.org/protobuf/internal/encoding/messageset"
"google.golang.org/protobuf/internal/genid"
"google.golang.org/protobuf/internal/pragma"
"google.golang.org/protobuf/internal/strs"
@ -207,7 +208,7 @@ type (
Number pref.FieldNumber
Cardinality pref.Cardinality // must be consistent with Message.RequiredNumbers
Kind pref.Kind
JSONName jsonName
StringName stringName
IsProto3Optional bool // promoted from google.protobuf.FieldDescriptorProto
IsWeak bool // promoted from google.protobuf.FieldOptions
HasPacked bool // promoted from google.protobuf.FieldOptions
@ -277,8 +278,9 @@ func (fd *Field) Options() pref.ProtoMessage {
func (fd *Field) Number() pref.FieldNumber { return fd.L1.Number }
func (fd *Field) Cardinality() pref.Cardinality { return fd.L1.Cardinality }
func (fd *Field) Kind() pref.Kind { return fd.L1.Kind }
func (fd *Field) HasJSONName() bool { return fd.L1.JSONName.has }
func (fd *Field) JSONName() string { return fd.L1.JSONName.get(fd) }
func (fd *Field) HasJSONName() bool { return fd.L1.StringName.hasJSON }
func (fd *Field) JSONName() string { return fd.L1.StringName.getJSON(fd) }
func (fd *Field) TextName() string { return fd.L1.StringName.getText(fd) }
func (fd *Field) HasPresence() bool {
return fd.L1.Cardinality != pref.Repeated && (fd.L0.ParentFile.L1.Syntax == pref.Proto2 || fd.L1.Message != nil || fd.L1.ContainingOneof != nil)
}
@ -373,7 +375,7 @@ type (
}
ExtensionL2 struct {
Options func() pref.ProtoMessage
JSONName jsonName
StringName stringName
IsProto3Optional bool // promoted from google.protobuf.FieldDescriptorProto
IsPacked bool // promoted from google.protobuf.FieldOptions
Default defaultValue
@ -391,8 +393,9 @@ func (xd *Extension) Options() pref.ProtoMessage {
func (xd *Extension) Number() pref.FieldNumber { return xd.L1.Number }
func (xd *Extension) Cardinality() pref.Cardinality { return xd.L1.Cardinality }
func (xd *Extension) Kind() pref.Kind { return xd.L1.Kind }
func (xd *Extension) HasJSONName() bool { return xd.lazyInit().JSONName.has }
func (xd *Extension) JSONName() string { return xd.lazyInit().JSONName.get(xd) }
func (xd *Extension) HasJSONName() bool { return xd.lazyInit().StringName.hasJSON }
func (xd *Extension) JSONName() string { return xd.lazyInit().StringName.getJSON(xd) }
func (xd *Extension) TextName() string { return xd.lazyInit().StringName.getText(xd) }
func (xd *Extension) HasPresence() bool { return xd.L1.Cardinality != pref.Repeated }
func (xd *Extension) HasOptionalKeyword() bool {
return (xd.L0.ParentFile.L1.Syntax == pref.Proto2 && xd.L1.Cardinality == pref.Optional) || xd.lazyInit().IsProto3Optional
@ -506,27 +509,50 @@ func (d *Base) Syntax() pref.Syntax { return d.L0.ParentFile.Syn
func (d *Base) IsPlaceholder() bool { return false }
func (d *Base) ProtoInternal(pragma.DoNotImplement) {}
type jsonName struct {
has bool
once sync.Once
name string
type stringName struct {
hasJSON bool
once sync.Once
nameJSON string
nameText string
}
// Init initializes the name. It is exported for use by other internal packages.
func (js *jsonName) Init(s string) {
js.has = true
js.name = s
// InitJSON initializes the name. It is exported for use by other internal packages.
func (s *stringName) InitJSON(name string) {
s.hasJSON = true
s.nameJSON = name
}
func (js *jsonName) get(fd pref.FieldDescriptor) string {
if !js.has {
js.once.Do(func() {
js.name = strs.JSONCamelCase(string(fd.Name()))
})
}
return js.name
func (s *stringName) lazyInit(fd pref.FieldDescriptor) *stringName {
s.once.Do(func() {
if fd.IsExtension() {
// For extensions, JSON and text are formatted the same way.
var name string
if messageset.IsMessageSetExtension(fd) {
name = string("[" + fd.FullName().Parent() + "]")
} else {
name = string("[" + fd.FullName() + "]")
}
s.nameJSON = name
s.nameText = name
} else {
// Format the JSON name.
if !s.hasJSON {
s.nameJSON = strs.JSONCamelCase(string(fd.Name()))
}
// Format the text name.
s.nameText = string(fd.Name())
if fd.Kind() == pref.GroupKind {
s.nameText = string(fd.Message().Name())
}
}
})
return s
}
func (s *stringName) getJSON(fd pref.FieldDescriptor) string { return s.lazyInit(fd).nameJSON }
func (s *stringName) getText(fd pref.FieldDescriptor) string { return s.lazyInit(fd).nameText }
func DefaultValue(v pref.Value, ev pref.EnumValueDescriptor) defaultValue {
dv := defaultValue{has: v.IsValid(), val: v, enum: ev}
if b, ok := v.Interface().([]byte); ok {

View File

@ -451,7 +451,7 @@ func (fd *Field) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd pref.Des
case genid.FieldDescriptorProto_Name_field_number:
fd.L0.FullName = appendFullName(sb, pd.FullName(), v)
case genid.FieldDescriptorProto_JsonName_field_number:
fd.L1.JSONName.Init(sb.MakeString(v))
fd.L1.StringName.InitJSON(sb.MakeString(v))
case genid.FieldDescriptorProto_DefaultValue_field_number:
fd.L1.Default.val = pref.ValueOfBytes(v) // temporarily store as bytes; later resolved in resolveMessages
case genid.FieldDescriptorProto_TypeName_field_number:
@ -551,7 +551,7 @@ func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) {
b = b[m:]
switch num {
case genid.FieldDescriptorProto_JsonName_field_number:
xd.L2.JSONName.Init(sb.MakeString(v))
xd.L2.StringName.InitJSON(sb.MakeString(v))
case genid.FieldDescriptorProto_DefaultValue_field_number:
xd.L2.Default.val = pref.ValueOfBytes(v) // temporarily store as bytes; later resolved in resolveExtensions
case genid.FieldDescriptorProto_TypeName_field_number:

View File

@ -245,6 +245,7 @@ type OneofFields struct {
once sync.Once
byName map[pref.Name]pref.FieldDescriptor // protected by once
byJSON map[string]pref.FieldDescriptor // protected by once
byText map[string]pref.FieldDescriptor // protected by once
byNum map[pref.FieldNumber]pref.FieldDescriptor // protected by once
}
@ -252,6 +253,7 @@ func (p *OneofFields) Len() int { return
func (p *OneofFields) Get(i int) pref.FieldDescriptor { return p.List[i] }
func (p *OneofFields) ByName(s pref.Name) pref.FieldDescriptor { return p.lazyInit().byName[s] }
func (p *OneofFields) ByJSONName(s string) pref.FieldDescriptor { return p.lazyInit().byJSON[s] }
func (p *OneofFields) ByTextName(s string) pref.FieldDescriptor { return p.lazyInit().byText[s] }
func (p *OneofFields) ByNumber(n pref.FieldNumber) pref.FieldDescriptor { return p.lazyInit().byNum[n] }
func (p *OneofFields) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) }
func (p *OneofFields) ProtoInternal(pragma.DoNotImplement) {}
@ -261,11 +263,13 @@ func (p *OneofFields) lazyInit() *OneofFields {
if len(p.List) > 0 {
p.byName = make(map[pref.Name]pref.FieldDescriptor, len(p.List))
p.byJSON = make(map[string]pref.FieldDescriptor, len(p.List))
p.byText = make(map[string]pref.FieldDescriptor, len(p.List))
p.byNum = make(map[pref.FieldNumber]pref.FieldDescriptor, len(p.List))
for _, f := range p.List {
// Field names and numbers are guaranteed to be unique.
p.byName[f.Name()] = f
p.byJSON[f.JSONName()] = f
p.byText[f.TextName()] = f
p.byNum[f.Number()] = f
}
}

View File

@ -142,6 +142,7 @@ type Fields struct {
once sync.Once
byName map[protoreflect.Name]*Field // protected by once
byJSON map[string]*Field // protected by once
byText map[string]*Field // protected by once
byNum map[protoreflect.FieldNumber]*Field // protected by once
}
@ -163,6 +164,12 @@ func (p *Fields) ByJSONName(s string) protoreflect.FieldDescriptor {
}
return nil
}
func (p *Fields) ByTextName(s string) protoreflect.FieldDescriptor {
if d := p.lazyInit().byText[s]; d != nil {
return d
}
return nil
}
func (p *Fields) ByNumber(n protoreflect.FieldNumber) protoreflect.FieldDescriptor {
if d := p.lazyInit().byNum[n]; d != nil {
return d
@ -178,6 +185,7 @@ func (p *Fields) lazyInit() *Fields {
if len(p.List) > 0 {
p.byName = make(map[protoreflect.Name]*Field, len(p.List))
p.byJSON = make(map[string]*Field, len(p.List))
p.byText = make(map[string]*Field, len(p.List))
p.byNum = make(map[protoreflect.FieldNumber]*Field, len(p.List))
for i := range p.List {
d := &p.List[i]
@ -187,6 +195,9 @@ func (p *Fields) lazyInit() *Fields {
if _, ok := p.byJSON[d.JSONName()]; !ok {
p.byJSON[d.JSONName()] = d
}
if _, ok := p.byText[d.TextName()]; !ok {
p.byText[d.TextName()] = d
}
if _, ok := p.byNum[d.Number()]; !ok {
p.byNum[d.Number()] = d
}

View File

@ -724,7 +724,7 @@ func testFileFormat(t *testing.T, fd pref.FileDescriptor) {
Number: 1000
Cardinality: repeated
Kind: message
JSONName: "X"
JSONName: "[test.C.X]"
IsExtension: true
IsList: true
Extendee: test.B
@ -745,7 +745,7 @@ func testFileFormat(t *testing.T, fd pref.FileDescriptor) {
Number: 1000
Cardinality: repeated
Kind: enum
JSONName: "X"
JSONName: "[test.X]"
IsExtension: true
IsPacked: true
IsList: true

View File

@ -154,7 +154,8 @@ func (x placeholderExtension) Number() pref.FieldNumber { retu
func (x placeholderExtension) Cardinality() pref.Cardinality { return 0 }
func (x placeholderExtension) Kind() pref.Kind { return 0 }
func (x placeholderExtension) HasJSONName() bool { return false }
func (x placeholderExtension) JSONName() string { return "" }
func (x placeholderExtension) JSONName() string { return "[" + string(x.name) + "]" }
func (x placeholderExtension) TextName() string { return "[" + string(x.name) + "]" }
func (x placeholderExtension) HasPresence() bool { return false }
func (x placeholderExtension) HasOptionalKeyword() bool { return false }
func (x placeholderExtension) IsExtension() bool { return true }

View File

@ -66,12 +66,7 @@ func appendMessage(b []byte, m protoreflect.Message) []byte {
b = append(b, '{')
order.RangeFields(m, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
k := string(fd.Name())
if fd.IsExtension() {
k = string("[" + fd.FullName() + "]")
}
b = append(b, k...)
b = append(b, fd.TextName()...)
b = append(b, ':')
b = appendValue(b, v, fd)
b = append(b, delim()...)

View File

@ -135,7 +135,7 @@ func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDesc
f.L1.Kind = protoreflect.Kind(fd.GetType())
}
if fd.JsonName != nil {
f.L1.JSONName.Init(fd.GetJsonName())
f.L1.StringName.InitJSON(fd.GetJsonName())
}
}
return fs, nil
@ -175,7 +175,7 @@ func (r descsByName) initExtensionDeclarations(xds []*descriptorpb.FieldDescript
x.L1.Kind = protoreflect.Kind(xd.GetType())
}
if xd.JsonName != nil {
x.L2.JSONName.Init(xd.GetJsonName())
x.L2.StringName.InitJSON(xd.GetJsonName())
}
}
return xs, nil

View File

@ -239,6 +239,9 @@ func validateExtensionDeclarations(xs []filedesc.Extension, xds []*descriptorpb.
return errors.New("extension field %q has an invalid cardinality: %d", x.FullName(), x.Cardinality())
}
if xd.JsonName != nil {
// A bug in older versions of protoc would always populate the
// "json_name" option for extensions when it is meaningless.
// When it did so, it would always use the camel-cased field name.
if xd.GetJsonName() != strs.JSONCamelCase(string(x.Name())) {
return errors.New("extension field %q may not have an explicitly set JSON name: %q", x.FullName(), xd.GetJsonName())
}

View File

@ -9,6 +9,7 @@ import (
"strings"
"google.golang.org/protobuf/internal/encoding/defval"
"google.golang.org/protobuf/internal/strs"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
@ -138,7 +139,14 @@ func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.Fi
p.TypeName = fullNameOf(field.Message())
}
if field.HasJSONName() {
p.JsonName = proto.String(field.JSONName())
// A bug in older versions of protoc would always populate the
// "json_name" option for extensions when it is meaningless.
// When it did so, it would always use the camel-cased field name.
if field.IsExtension() {
p.JsonName = proto.String(strs.JSONCamelCase(string(field.Name())))
} else {
p.JsonName = proto.String(field.JSONName())
}
}
if field.Syntax() == protoreflect.Proto3 && field.HasOptionalKeyword() {
p.Proto3Optional = proto.Bool(true)

View File

@ -279,8 +279,15 @@ type FieldDescriptor interface {
// JSONName reports the name used for JSON serialization.
// It is usually the camel-cased form of the field name.
// Extension fields are represented by the full name surrounded by brackets.
JSONName() string
// TextName reports the name used for text serialization.
// It is usually the name of the field, except that groups use the name
// of the inlined message, and extension fields are represented by the
// full name surrounded by brackets.
TextName() string
// HasPresence reports whether the field distinguishes between unpopulated
// and default values.
HasPresence() bool
@ -371,6 +378,9 @@ type FieldDescriptors interface {
// ByJSONName returns the FieldDescriptor for a field with s as the JSON name.
// It returns nil if not found.
ByJSONName(s string) FieldDescriptor
// ByTextName returns the FieldDescriptor for a field with s as the text name.
// It returns nil if not found.
ByTextName(s string) FieldDescriptor
// ByNumber returns the FieldDescriptor for a field numbered n.
// It returns nil if not found.
ByNumber(n FieldNumber) FieldDescriptor

View File

@ -42,10 +42,7 @@ func (m reflectMessage) stringKey(fd protoreflect.FieldDescriptor) string {
if m.Descriptor() != fd.ContainingMessage() {
panic("mismatching containing message")
}
if fd.IsExtension() {
return string("[" + fd.FullName() + "]")
}
return string(fd.Name())
return fd.TextName()
}
func (m reflectMessage) Descriptor() protoreflect.MessageDescriptor {

View File

@ -167,7 +167,7 @@ func IgnoreDescriptors(descs ...protoreflect.Descriptor) cmp.Option {
func mustFindFieldDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.FieldDescriptor {
d := findDescriptor(md, s)
if fd, ok := d.(protoreflect.FieldDescriptor); ok && fd.Name() == s {
if fd, ok := d.(protoreflect.FieldDescriptor); ok && fd.TextName() == string(s) {
return fd
}
@ -199,10 +199,10 @@ func mustFindOneofDescriptor(md protoreflect.MessageDescriptor, s protoreflect.N
func findDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.Descriptor {
// Exact match.
if fd := md.Fields().ByName(s); fd != nil {
if fd := md.Fields().ByTextName(string(s)); fd != nil {
return fd
}
if od := md.Oneofs().ByName(s); od != nil {
if od := md.Oneofs().ByName(s); od != nil && !od.IsSynthetic() {
return od
}
@ -293,13 +293,18 @@ func (f *nameFilters) filterFields(p cmp.Path) bool {
}
func (f *nameFilters) filterFieldName(m Message, k string) bool {
if md := m.Descriptor(); md != nil {
switch {
case protoreflect.Name(k).IsValid():
return f.names[md.Fields().ByName(protoreflect.Name(k)).FullName()]
case strings.HasPrefix(k, "[") && strings.HasSuffix(k, "]"):
return f.names[protoreflect.FullName(k[1:len(k)-1])]
}
if _, ok := m[k]; !ok {
return true // treat missing fields as already filtered
}
var fd protoreflect.FieldDescriptor
switch mt := m[messageTypeKey].(messageType); {
case protoreflect.Name(k).IsValid():
fd = mt.md.Fields().ByTextName(k)
default:
fd = mt.xds[k]
}
if fd != nil {
return f.names[fd.FullName()]
}
return false
}
@ -373,9 +378,9 @@ func isDefaultScalar(m Message, k string) bool {
var fd protoreflect.FieldDescriptor
switch mt := m[messageTypeKey].(messageType); {
case protoreflect.Name(k).IsValid():
fd = mt.md.Fields().ByName(protoreflect.Name(k))
case strings.HasPrefix(k, "[") && strings.HasSuffix(k, "]"):
fd = mt.xds[protoreflect.FullName(k[1:len(k)-1])]
fd = mt.md.Fields().ByTextName(k)
default:
fd = mt.xds[k]
}
if fd == nil || !fd.Default().IsValid() {
return false

View File

@ -74,7 +74,7 @@ const (
type messageType struct {
md protoreflect.MessageDescriptor
xds map[protoreflect.FullName]protoreflect.ExtensionDescriptor
xds map[string]protoreflect.ExtensionDescriptor
}
func (t messageType) String() string {
@ -218,14 +218,13 @@ func isMessageType(t reflect.Type) bool {
func transformMessage(m protoreflect.Message) Message {
mx := Message{}
mt := messageType{md: m.Descriptor(), xds: make(map[protoreflect.FullName]protoreflect.FieldDescriptor)}
mt := messageType{md: m.Descriptor(), xds: make(map[string]protoreflect.FieldDescriptor)}
// Handle known and extension fields.
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
s := string(fd.Name())
s := fd.TextName()
if fd.IsExtension() {
s = "[" + string(fd.FullName()) + "]"
mt.xds[fd.FullName()] = fd
mt.xds[s] = fd
}
switch {
case fd.IsList():