golang / protobuf

Go support for Google's protocol buffers
BSD 3-Clause "New" or "Revised" License
9.66k stars 1.58k forks source link

export protojson "typeFieldDesc" #1497

Closed Patrick0308 closed 1 year ago

Patrick0308 commented 1 year ago

Is your feature request related to a problem? Please describe. I want to write a myself protojson marshaler. I cannot implement protoreflect.FieldDescriptor. It's not a problem. However I also want implement typeURLFieldRanger. I cannot do it. In protojson encoder define the typeFieldDesc.

// typeFieldDesc is a synthetic field descriptor used for the "@type" field.
var typeFieldDesc = func() protoreflect.FieldDescriptor {
    var fd filedesc.Field
    fd.L0.FullName = "@type"
    fd.L0.Index = -1
    fd.L1.Cardinality = protoreflect.Optional
    fd.L1.Kind = protoreflect.StringKind
    return &fd
}()

https://github.com/protocolbuffers/protobuf-go/blob/7a48e2b66218ba306ed801e42126b374aefce255/encoding/protojson/encode.go#L150

Describe the solution you'd like Move "typeFieldDesc" to reflect package or other package then export it. Or just export it on protojson package.

Describe alternatives you've considered

Additional context

aktau commented 1 year ago

I need to look at this in a bit more detail to see if this is an appropriate solution, but let's first take a step back: why are you writing your own protojson marshaller? What is missing from package protojson?

Patrick0308 commented 1 year ago

@aktau Thanks, for your reply. We want to add some protojson decode rules to support the ways we use. Now our grpc service unmarshal the json requests from http gateway to protobuf parameter by jsonproto. Http gateway will transfer request's url parameter to json. This an transfer example:

 http://github.com/golang/protobuf/issues/1497?p1=true

Http gateway know 1497 is issueId. So gateway will generate the json requests

{
   "issueId": "1497",
   "p1":"true"
}

We want to protojson can unmarshal "true" to bool field successfully. There are other decode rules to support this way. And we also hope own protojson unmarshaler and marshaler compatible with official protojson.

Patrick0308 commented 1 year ago

Actually now I can implement own protojson unmarshaler, but marshaler cant because of typeURLFieldRanger. In the scenario we use, I can use official marshaler and own unmarshaler. But I think it will confuses others. So I want to implement own marshaler too.

dsnet commented 1 year ago

You don't need access to this type, you can construct it locally:

// typeFieldDesc is a synthetic field descriptor used for the "@type" field.
type typeFieldDesc struct{ protoreflect.FieldDescriptor }

func (typeFieldDesc) Kind() protoreflect.Kind         { return protoreflect.StringKind }
func (typeFieldDesc) Index() int                      { return -1 }
func (typeFieldDesc) FullName() protoreflect.FullName { return "@type" }
func (typeFieldDesc) JSONName() string                { return "@type" }
func (typeFieldDesc) TextName() string                { return "@type" }
func (typeFieldDesc) IsMap() bool                     { return false }
func (typeFieldDesc) IsList() bool                    { return false }
func (typeFieldDesc) IsExtension() bool               { return false }

And modify the typeURLFieldRanger.Range method as follows:

  func (m typeURLFieldRanger) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) {
-   if !f(typeFieldDesc, protoreflect.ValueOfString(m.typeURL)) {
+   if !f(typeFieldDesc{}, protoreflect.ValueOfString(m.typeURL)) {
        return
    }
    m.FieldRanger.Range(f)
  }
Patrick0308 commented 1 year ago

@dsnet Thanks, it works.