github.com/erda-project/erda-infra@v1.0.10-0.20240327085753-f3a249292aeb/pkg/transport/http/encoding/jsonpb/encode.go (about)

     1  // Copyright (c) 2021 Terminus, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package jsonpb
    16  
    17  import (
    18  	"encoding/json"
    19  	"errors"
    20  	"fmt"
    21  	"io"
    22  	"math"
    23  	"reflect"
    24  	"sort"
    25  	"strconv"
    26  	"strings"
    27  	"time"
    28  
    29  	"github.com/golang/protobuf/proto"
    30  	protoV2 "google.golang.org/protobuf/proto"
    31  	"google.golang.org/protobuf/reflect/protoreflect"
    32  	"google.golang.org/protobuf/reflect/protoregistry"
    33  )
    34  
    35  // Marshaler is a configurable object for marshaling protocol buffer messages
    36  // to the specified JSON representation.
    37  type Marshaler struct {
    38  	// OrigName specifies whether to use the original protobuf name for fields.
    39  	OrigName bool
    40  
    41  	// EnumsAsInts specifies whether to render enum values as integers,
    42  	// as opposed to string values.
    43  	EnumsAsInts bool
    44  
    45  	// EmitDefaults specifies whether to render fields with zero values.
    46  	EmitDefaults bool
    47  
    48  	// Indent controls whether the output is compact or not.
    49  	// If empty, the output is compact JSON. Otherwise, every JSON object
    50  	// entry and JSON array value will be on its own line.
    51  	// Each line will be preceded by repeated copies of Indent, where the
    52  	// number of copies is the current indentation depth.
    53  	Indent string
    54  
    55  	// AnyResolver is used to resolve the google.protobuf.Any well-known type.
    56  	// If unset, the global registry is used by default.
    57  	AnyResolver AnyResolver
    58  }
    59  
    60  // IMarshaler is implemented by protobuf messages that customize the
    61  // way they are marshaled to JSON. Messages that implement this should also
    62  // implement JSONPBUnmarshaler so that the custom format can be parsed.
    63  //
    64  // The JSON marshaling must follow the proto to JSON specification:
    65  //
    66  //	https://developers.google.com/protocol-buffers/docs/proto3#json
    67  //
    68  // Deprecated: Custom types should implement protobuf reflection instead.
    69  type IMarshaler interface {
    70  	MarshalJSONPB(*Marshaler) ([]byte, error)
    71  }
    72  
    73  // Marshal serializes a protobuf message as JSON into w.
    74  func (jm *Marshaler) Marshal(w io.Writer, m proto.Message) error {
    75  	b, err := jm.marshal(m)
    76  	if len(b) > 0 {
    77  		if _, err := w.Write(b); err != nil {
    78  			return err
    79  		}
    80  	}
    81  	return err
    82  }
    83  
    84  // MarshalToString serializes a protobuf message as JSON in string form.
    85  func (jm *Marshaler) MarshalToString(m proto.Message) (string, error) {
    86  	b, err := jm.marshal(m)
    87  	if err != nil {
    88  		return "", err
    89  	}
    90  	return string(b), nil
    91  }
    92  
    93  func (jm *Marshaler) marshal(m proto.Message) ([]byte, error) {
    94  	v := reflect.ValueOf(m)
    95  	if m == nil || (v.Kind() == reflect.Ptr && v.IsNil()) {
    96  		return nil, errors.New("Marshal called with nil")
    97  	}
    98  
    99  	// Check for custom marshalers first since they may not properly
   100  	// implement protobuf reflection that the logic below relies on.
   101  	if jsm, ok := m.(IMarshaler); ok {
   102  		return jsm.MarshalJSONPB(jm)
   103  	}
   104  
   105  	// Check for unpopulated required fields first.
   106  	m2 := proto.MessageReflect(m)
   107  	if err := protoV2.CheckInitialized(m2.Interface()); err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	w := jsonWriter{Marshaler: jm}
   112  	err := w.marshalMessage(m2, "", "")
   113  	return w.buf, err
   114  }
   115  
   116  type jsonWriter struct {
   117  	*Marshaler
   118  	buf []byte
   119  }
   120  
   121  func (w *jsonWriter) write(s string) {
   122  	w.buf = append(w.buf, s...)
   123  }
   124  
   125  func (w *jsonWriter) marshalMessage(m protoreflect.Message, indent, typeURL string) error {
   126  	if jsm, ok := proto.MessageV1(m.Interface()).(IMarshaler); ok {
   127  		b, err := jsm.MarshalJSONPB(w.Marshaler)
   128  		if err != nil {
   129  			return err
   130  		}
   131  		if typeURL != "" {
   132  			// we are marshaling this object to an Any type
   133  			var js map[string]*json.RawMessage
   134  			if err = json.Unmarshal(b, &js); err != nil {
   135  				return fmt.Errorf("type %T produced invalid JSON: %v", m.Interface(), err)
   136  			}
   137  			turl, err := json.Marshal(typeURL)
   138  			if err != nil {
   139  				return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err)
   140  			}
   141  			js["@type"] = (*json.RawMessage)(&turl)
   142  			if b, err = json.Marshal(js); err != nil {
   143  				return err
   144  			}
   145  		}
   146  		w.write(string(b))
   147  		return nil
   148  	}
   149  
   150  	md := m.Descriptor()
   151  	fds := md.Fields()
   152  
   153  	// Handle well-known types.
   154  	const secondInNanos = int64(time.Second / time.Nanosecond)
   155  	switch wellKnownType(md.FullName()) {
   156  	case "Any":
   157  		return w.marshalAny(m, indent)
   158  	case "BoolValue", "BytesValue", "StringValue",
   159  		"Int32Value", "UInt32Value", "FloatValue",
   160  		"Int64Value", "UInt64Value", "DoubleValue":
   161  		fd := fds.ByNumber(1)
   162  		return w.marshalValue(fd, m.Get(fd), indent)
   163  	case "Duration":
   164  		const maxSecondsInDuration = 315576000000
   165  		// "Generated output always contains 0, 3, 6, or 9 fractional digits,
   166  		//  depending on required precision."
   167  		s := m.Get(fds.ByNumber(1)).Int()
   168  		ns := m.Get(fds.ByNumber(2)).Int()
   169  		if s < -maxSecondsInDuration || s > maxSecondsInDuration {
   170  			return fmt.Errorf("seconds out of range %v", s)
   171  		}
   172  		if ns <= -secondInNanos || ns >= secondInNanos {
   173  			return fmt.Errorf("ns out of range (%v, %v)", -secondInNanos, secondInNanos)
   174  		}
   175  		if (s > 0 && ns < 0) || (s < 0 && ns > 0) {
   176  			return errors.New("signs of seconds and nanos do not match")
   177  		}
   178  		var sign string
   179  		if s < 0 || ns < 0 {
   180  			sign, s, ns = "-", -1*s, -1*ns
   181  		}
   182  		x := fmt.Sprintf("%s%d.%09d", sign, s, ns)
   183  		x = strings.TrimSuffix(x, "000")
   184  		x = strings.TrimSuffix(x, "000")
   185  		x = strings.TrimSuffix(x, ".000")
   186  		w.write(fmt.Sprintf(`"%vs"`, x))
   187  		return nil
   188  	case "Timestamp":
   189  		// "RFC 3339, where generated output will always be Z-normalized
   190  		//  and uses 0, 3, 6 or 9 fractional digits."
   191  		s := m.Get(fds.ByNumber(1)).Int()
   192  		ns := m.Get(fds.ByNumber(2)).Int()
   193  		if ns < 0 || ns >= secondInNanos {
   194  			return fmt.Errorf("ns out of range [0, %v)", secondInNanos)
   195  		}
   196  		t := time.Unix(s, ns).UTC()
   197  		// time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits).
   198  		x := t.Format("2006-01-02T15:04:05.000000000")
   199  		x = strings.TrimSuffix(x, "000")
   200  		x = strings.TrimSuffix(x, "000")
   201  		x = strings.TrimSuffix(x, ".000")
   202  		w.write(fmt.Sprintf(`"%vZ"`, x))
   203  		return nil
   204  	case "Value":
   205  		// JSON value; which is a null, number, string, bool, object, or array.
   206  		od := md.Oneofs().Get(0)
   207  		fd := m.WhichOneof(od)
   208  		if fd == nil {
   209  			return errors.New("nil Value")
   210  		}
   211  		return w.marshalValue(fd, m.Get(fd), indent)
   212  	case "Struct", "ListValue":
   213  		// JSON object or array.
   214  		fd := fds.ByNumber(1)
   215  		return w.marshalValue(fd, m.Get(fd), indent)
   216  	}
   217  
   218  	w.write("{")
   219  	if w.Indent != "" {
   220  		w.write("\n")
   221  	}
   222  
   223  	firstField := true
   224  	if typeURL != "" {
   225  		if err := w.marshalTypeURL(indent, typeURL); err != nil {
   226  			return err
   227  		}
   228  		firstField = false
   229  	}
   230  
   231  	for i := 0; i < fds.Len(); {
   232  		fd := fds.Get(i)
   233  		if od := fd.ContainingOneof(); od != nil {
   234  			fd = m.WhichOneof(od)
   235  			i += od.Fields().Len()
   236  			if fd == nil {
   237  				continue
   238  			}
   239  		} else {
   240  			i++
   241  		}
   242  
   243  		v := m.Get(fd)
   244  
   245  		if !m.Has(fd) {
   246  			if !w.EmitDefaults || fd.ContainingOneof() != nil {
   247  				continue
   248  			}
   249  			if fd.Cardinality() != protoreflect.Repeated && (fd.Message() != nil || fd.Syntax() == protoreflect.Proto2) {
   250  				v = protoreflect.Value{} // use "null" for singular messages or proto2 scalars
   251  			}
   252  		}
   253  
   254  		if !firstField {
   255  			w.writeComma()
   256  		}
   257  		if err := w.marshalField(fd, v, indent); err != nil {
   258  			return err
   259  		}
   260  		firstField = false
   261  	}
   262  
   263  	// Handle proto2 extensions.
   264  	if md.ExtensionRanges().Len() > 0 {
   265  		// Collect a sorted list of all extension descriptor and values.
   266  		type ext struct {
   267  			desc protoreflect.FieldDescriptor
   268  			val  protoreflect.Value
   269  		}
   270  		var exts []ext
   271  		m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
   272  			if fd.IsExtension() {
   273  				exts = append(exts, ext{fd, v})
   274  			}
   275  			return true
   276  		})
   277  		sort.Slice(exts, func(i, j int) bool {
   278  			return exts[i].desc.Number() < exts[j].desc.Number()
   279  		})
   280  
   281  		for _, ext := range exts {
   282  			if !firstField {
   283  				w.writeComma()
   284  			}
   285  			if err := w.marshalField(ext.desc, ext.val, indent); err != nil {
   286  				return err
   287  			}
   288  			firstField = false
   289  		}
   290  	}
   291  
   292  	if w.Indent != "" {
   293  		w.write("\n")
   294  		w.write(indent)
   295  	}
   296  	w.write("}")
   297  	return nil
   298  }
   299  
   300  func (w *jsonWriter) writeComma() {
   301  	if w.Indent != "" {
   302  		w.write(",\n")
   303  	} else {
   304  		w.write(",")
   305  	}
   306  }
   307  
   308  func (w *jsonWriter) marshalAny(m protoreflect.Message, indent string) error {
   309  	// "If the Any contains a value that has a special JSON mapping,
   310  	//  it will be converted as follows: {"@type": xxx, "value": yyy}.
   311  	//  Otherwise, the value will be converted into a JSON object,
   312  	//  and the "@type" field will be inserted to indicate the actual data type."
   313  	md := m.Descriptor()
   314  	typeURL := m.Get(md.Fields().ByNumber(1)).String()
   315  	rawVal := m.Get(md.Fields().ByNumber(2)).Bytes()
   316  
   317  	var m2 protoreflect.Message
   318  	if w.AnyResolver != nil {
   319  		mi, err := w.AnyResolver.Resolve(typeURL)
   320  		if err != nil {
   321  			return err
   322  		}
   323  		m2 = proto.MessageReflect(mi)
   324  	} else {
   325  		mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL)
   326  		if err != nil {
   327  			return err
   328  		}
   329  		m2 = mt.New()
   330  	}
   331  
   332  	if err := protoV2.Unmarshal(rawVal, m2.Interface()); err != nil {
   333  		return err
   334  	}
   335  
   336  	if wellKnownType(m2.Descriptor().FullName()) == "" {
   337  		return w.marshalMessage(m2, indent, typeURL)
   338  	}
   339  
   340  	w.write("{")
   341  	if w.Indent != "" {
   342  		w.write("\n")
   343  	}
   344  	if err := w.marshalTypeURL(indent, typeURL); err != nil {
   345  		return err
   346  	}
   347  	w.writeComma()
   348  	if w.Indent != "" {
   349  		w.write(indent)
   350  		w.write(w.Indent)
   351  		w.write(`"value": `)
   352  	} else {
   353  		w.write(`"value":`)
   354  	}
   355  	if err := w.marshalMessage(m2, indent+w.Indent, ""); err != nil {
   356  		return err
   357  	}
   358  	if w.Indent != "" {
   359  		w.write("\n")
   360  		w.write(indent)
   361  	}
   362  	w.write("}")
   363  	return nil
   364  }
   365  
   366  func (w *jsonWriter) marshalTypeURL(indent, typeURL string) error {
   367  	if w.Indent != "" {
   368  		w.write(indent)
   369  		w.write(w.Indent)
   370  	}
   371  	w.write(`"@type":`)
   372  	if w.Indent != "" {
   373  		w.write(" ")
   374  	}
   375  	b, err := json.Marshal(typeURL)
   376  	if err != nil {
   377  		return err
   378  	}
   379  	w.write(string(b))
   380  	return nil
   381  }
   382  
   383  // marshalField writes field description and value to the Writer.
   384  func (w *jsonWriter) marshalField(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
   385  	if w.Indent != "" {
   386  		w.write(indent)
   387  		w.write(w.Indent)
   388  	}
   389  	w.write(`"`)
   390  	switch {
   391  	case fd.IsExtension():
   392  		// For message set, use the fname of the message as the extension name.
   393  		name := string(fd.FullName())
   394  		if isMessageSet(fd.ContainingMessage()) {
   395  			name = strings.TrimSuffix(name, ".message_set_extension")
   396  		}
   397  
   398  		w.write("[" + name + "]")
   399  	case w.OrigName:
   400  		name := string(fd.Name())
   401  		if fd.Kind() == protoreflect.GroupKind {
   402  			name = string(fd.Message().Name())
   403  		}
   404  		w.write(name)
   405  	default:
   406  		w.write(string(fd.JSONName()))
   407  	}
   408  	w.write(`":`)
   409  	if w.Indent != "" {
   410  		w.write(" ")
   411  	}
   412  	return w.marshalValue(fd, v, indent)
   413  }
   414  
   415  func (w *jsonWriter) marshalValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
   416  	switch {
   417  	case fd.IsList():
   418  		w.write("[")
   419  		comma := ""
   420  		lv := v.List()
   421  		for i := 0; i < lv.Len(); i++ {
   422  			w.write(comma)
   423  			if w.Indent != "" {
   424  				w.write("\n")
   425  				w.write(indent)
   426  				w.write(w.Indent)
   427  				w.write(w.Indent)
   428  			}
   429  			if err := w.marshalSingularValue(fd, lv.Get(i), indent+w.Indent); err != nil {
   430  				return err
   431  			}
   432  			comma = ","
   433  		}
   434  		if w.Indent != "" {
   435  			w.write("\n")
   436  			w.write(indent)
   437  			w.write(w.Indent)
   438  		}
   439  		w.write("]")
   440  		return nil
   441  	case fd.IsMap():
   442  		kfd := fd.MapKey()
   443  		vfd := fd.MapValue()
   444  		mv := v.Map()
   445  
   446  		// Collect a sorted list of all map keys and values.
   447  		type entry struct{ key, val protoreflect.Value }
   448  		var entries []entry
   449  		mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
   450  			entries = append(entries, entry{k.Value(), v})
   451  			return true
   452  		})
   453  		sort.Slice(entries, func(i, j int) bool {
   454  			switch kfd.Kind() {
   455  			case protoreflect.BoolKind:
   456  				return !entries[i].key.Bool() && entries[j].key.Bool()
   457  			case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
   458  				return entries[i].key.Int() < entries[j].key.Int()
   459  			case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
   460  				return entries[i].key.Uint() < entries[j].key.Uint()
   461  			case protoreflect.StringKind:
   462  				return entries[i].key.String() < entries[j].key.String()
   463  			default:
   464  				panic("invalid kind")
   465  			}
   466  		})
   467  
   468  		w.write(`{`)
   469  		comma := ""
   470  		for _, entry := range entries {
   471  			w.write(comma)
   472  			if w.Indent != "" {
   473  				w.write("\n")
   474  				w.write(indent)
   475  				w.write(w.Indent)
   476  				w.write(w.Indent)
   477  			}
   478  
   479  			s := fmt.Sprint(entry.key.Interface())
   480  			b, err := json.Marshal(s)
   481  			if err != nil {
   482  				return err
   483  			}
   484  			w.write(string(b))
   485  
   486  			w.write(`:`)
   487  			if w.Indent != "" {
   488  				w.write(` `)
   489  			}
   490  
   491  			if err := w.marshalSingularValue(vfd, entry.val, indent+w.Indent); err != nil {
   492  				return err
   493  			}
   494  			comma = ","
   495  		}
   496  		if w.Indent != "" {
   497  			w.write("\n")
   498  			w.write(indent)
   499  			w.write(w.Indent)
   500  		}
   501  		w.write(`}`)
   502  		return nil
   503  	default:
   504  		return w.marshalSingularValue(fd, v, indent)
   505  	}
   506  }
   507  
   508  func (w *jsonWriter) marshalSingularValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
   509  	switch {
   510  	case !v.IsValid():
   511  		w.write("null")
   512  		return nil
   513  	case fd.Message() != nil:
   514  		return w.marshalMessage(v.Message(), indent+w.Indent, "")
   515  	case fd.Enum() != nil:
   516  		if fd.Enum().FullName() == "google.protobuf.NullValue" {
   517  			w.write("null")
   518  			return nil
   519  		}
   520  
   521  		vd := fd.Enum().Values().ByNumber(v.Enum())
   522  		if vd == nil || w.EnumsAsInts {
   523  			w.write(strconv.Itoa(int(v.Enum())))
   524  		} else {
   525  			w.write(`"` + string(vd.Name()) + `"`)
   526  		}
   527  		return nil
   528  	default:
   529  		switch v.Interface().(type) {
   530  		case float32, float64:
   531  			switch {
   532  			case math.IsInf(v.Float(), +1):
   533  				w.write(`"Infinity"`)
   534  				return nil
   535  			case math.IsInf(v.Float(), -1):
   536  				w.write(`"-Infinity"`)
   537  				return nil
   538  			case math.IsNaN(v.Float()):
   539  				w.write(`"NaN"`)
   540  				return nil
   541  			}
   542  		case int64, uint64:
   543  			w.write(fmt.Sprintf(`%d`, v.Interface()))
   544  			return nil
   545  		}
   546  
   547  		b, err := json.Marshal(v.Interface())
   548  		if err != nil {
   549  			return err
   550  		}
   551  		w.write(string(b))
   552  		return nil
   553  	}
   554  }