github.com/DoNewsCode/core@v0.12.3/codec/json/json.go (about)

     1  // Package json provides the json codec.
     2  //
     3  // The code and tests in this package is derived from
     4  // https://github.com/go-kratos/kratos under MIT license
     5  // https://github.com/go-kratos/kratos/blob/main/LICENSE
     6  package json
     7  
     8  import (
     9  	"encoding/json"
    10  	"reflect"
    11  
    12  	"google.golang.org/protobuf/encoding/protojson"
    13  	"google.golang.org/protobuf/proto"
    14  )
    15  
    16  // Codec is a Codec implementation with json.
    17  type Codec struct {
    18  	prefix           string
    19  	indent           string
    20  	marshalOptions   protojson.MarshalOptions
    21  	unmarshalOptions protojson.UnmarshalOptions
    22  }
    23  
    24  // Option is the type of functional options to codec
    25  type Option func(*Codec)
    26  
    27  // NewCodec creates a new json codec
    28  func NewCodec(opts ...Option) Codec {
    29  	var (
    30  		codec          Codec
    31  		marshalOptions = protojson.MarshalOptions{
    32  			EmitUnpopulated: true,
    33  		}
    34  		unmarshalOptions = protojson.UnmarshalOptions{
    35  			DiscardUnknown: true,
    36  		}
    37  	)
    38  	codec.marshalOptions = marshalOptions
    39  	codec.unmarshalOptions = unmarshalOptions
    40  	for _, f := range opts {
    41  		f(&codec)
    42  	}
    43  	return codec
    44  }
    45  
    46  // WithIndent allows the codec to indent json output while marshalling. It is
    47  // useful when the json output is meant for humans to read.
    48  func WithIndent(indent string) Option {
    49  	return func(codec *Codec) {
    50  		codec.indent = indent
    51  		codec.marshalOptions.Multiline = true
    52  		codec.marshalOptions.Indent = indent
    53  	}
    54  }
    55  
    56  // Marshal serialize the interface{} to []byte
    57  func (c Codec) Marshal(v interface{}) ([]byte, error) {
    58  	if m, ok := v.(proto.Message); ok {
    59  		return c.marshalOptions.Marshal(m)
    60  	}
    61  	if c.indent != "" {
    62  		return json.MarshalIndent(v, c.prefix, c.indent)
    63  	}
    64  	return json.Marshal(v)
    65  }
    66  
    67  // Unmarshal deserialize the []byte to interface{}
    68  func (c Codec) Unmarshal(data []byte, v interface{}) error {
    69  	rv := reflect.ValueOf(v)
    70  	for rv.Kind() == reflect.Ptr {
    71  		if rv.IsNil() {
    72  			rv.Set(reflect.New(rv.Type().Elem()))
    73  		}
    74  		rv = rv.Elem()
    75  	}
    76  	if m, ok := v.(proto.Message); ok {
    77  		return c.unmarshalOptions.Unmarshal(data, m)
    78  	} else if m, ok := reflect.Indirect(reflect.ValueOf(v)).Interface().(proto.Message); ok {
    79  		return c.unmarshalOptions.Unmarshal(data, m)
    80  	}
    81  	return json.Unmarshal(data, v)
    82  }