encoding: add Format helper function and method
The Format function and MarshalOptions.Format method are helper
functions for directly obtaining the formatted string for a message
without having to deal with errors or convert a []byte to string.
It is only intended for human consumption (e.g., debugging or logging).
We also add a MarshalOptions.Multiline option to specify that the output
should use some default indentation in a multiline output.
This assists in the v1 to v2 migration where:
protoV1.CompactTextString(m) => prototext.MarshalOptions{}.Format(m)
protoV1.MarshalTextString(m) => prototext.Format(m)
At Google, there are approximately 10x more usages of MarshalTextString than
CompactTextString, so it makes sense that the top-level Format function
does multiline expansion by default.
Fixes #850
Change-Id: I149c9e190a6d99b985d3884df675499a3313e9b3
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/213460
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/encoding/protojson/encode.go b/encoding/protojson/encode.go
index 9b2592d..02723c0 100644
--- a/encoding/protojson/encode.go
+++ b/encoding/protojson/encode.go
@@ -19,6 +19,16 @@
"google.golang.org/protobuf/reflect/protoregistry"
)
+const defaultIndent = " "
+
+// Format formats the message as a multiline string.
+// This function is only intended for human consumption and ignores errors.
+// Do not depend on the output being stable. It may change over time across
+// different versions of the program.
+func Format(m proto.Message) string {
+ return MarshalOptions{Multiline: true}.Format(m)
+}
+
// Marshal writes the given proto.Message in JSON format using default options.
// Do not depend on the output being stable. It may change over time across
// different versions of the program.
@@ -59,9 +69,15 @@
// ╚═══════╧════════════════════════════╝
EmitUnpopulated bool
- // If Indent is a non-empty string, it causes entries for an Array or Object
- // to be preceded by the indent and trailed by a newline. Indent can only be
- // composed of space or tab characters.
+ // Multiline specifies whether the marshaler should format the output in
+ // indented-form with every textual element on a new line.
+ // If Indent is an empty string, then an arbitrary indent is chosen.
+ Multiline bool
+
+ // Indent specifies the set of indentation characters to use in a multiline
+ // formatted output such that every entry is preceded by Indent and
+ // terminated by a newline. If non-empty, then Multiline is treated as true.
+ // Indent can only be composed of space or tab characters.
Indent string
// Resolver is used for looking up types when expanding google.protobuf.Any
@@ -72,18 +88,35 @@
}
}
+// Format formats the message as a string.
+// This method is only intended for human consumption and ignores errors.
+// Do not depend on the output being stable. It may change over time across
+// different versions of the program.
+func (o MarshalOptions) Format(m proto.Message) string {
+ if m == nil || !m.ProtoReflect().IsValid() {
+ return "<nil>" // invalid syntax, but okay since this is for debugging
+ }
+ o.AllowPartial = true
+ b, _ := o.Marshal(m)
+ return string(b)
+}
+
// Marshal marshals the given proto.Message in the JSON format using options in
// MarshalOptions. Do not depend on the output being stable. It may change over
// time across different versions of the program.
func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
+ if o.Multiline && o.Indent == "" {
+ o.Indent = defaultIndent
+ }
+ if o.Resolver == nil {
+ o.Resolver = protoregistry.GlobalTypes
+ }
+
var err error
o.encoder, err = json.NewEncoder(o.Indent)
if err != nil {
return nil, err
}
- if o.Resolver == nil {
- o.Resolver = protoregistry.GlobalTypes
- }
err = o.marshalMessage(m.ProtoReflect())
if err != nil {