mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-02-14 15:40:24 +00:00
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:
parent
467a9cdc10
commit
e14d6b3cdc
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
@ -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()...)
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
|
Loading…
x
Reference in New Issue
Block a user