go.temporal.io/server@v1.23.0/common/codec/jsonpb.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package codec 26 27 import ( 28 "bytes" 29 "encoding/json" 30 "fmt" 31 32 historypb "go.temporal.io/api/history/v1" 33 "go.temporal.io/api/temporalproto" 34 "google.golang.org/protobuf/encoding/protojson" 35 "google.golang.org/protobuf/proto" 36 ) 37 38 type ( 39 // JSONPBEncoder is JSON encoder/decoder for protobuf structs and slices of protobuf structs. 40 // This is an wrapper on top of jsonpb.Marshaler which supports not only single object serialization 41 // but also slices of concrete objects. 42 JSONPBEncoder struct { 43 marshaler protojson.MarshalOptions 44 unmarshaler temporalproto.CustomJSONUnmarshalOptions 45 } 46 ) 47 48 // NewJSONPBEncoder creates a new JSONPBEncoder. 49 func NewJSONPBEncoder() JSONPBEncoder { 50 return JSONPBEncoder{} 51 } 52 53 // NewJSONPBIndentEncoder creates a new JSONPBEncoder with indent. 54 func NewJSONPBIndentEncoder(indent string) JSONPBEncoder { 55 return JSONPBEncoder{ 56 marshaler: protojson.MarshalOptions{ 57 Indent: indent, 58 }, 59 } 60 } 61 62 // Encode protobuf struct to bytes. 63 func (e JSONPBEncoder) Encode(pb proto.Message) ([]byte, error) { 64 return e.marshaler.Marshal(pb) 65 } 66 67 // Decode bytes to protobuf struct. 68 func (e JSONPBEncoder) Decode(data []byte, pb proto.Message) error { 69 return e.unmarshaler.Unmarshal(data, pb) 70 } 71 72 // Encode HistoryEvent slice to bytes. 73 func (e *JSONPBEncoder) EncodeHistoryEvents(historyEvents []*historypb.HistoryEvent) ([]byte, error) { 74 return e.encodeSlice( 75 len(historyEvents), 76 func(i int) proto.Message { return historyEvents[i] }) 77 } 78 79 // Encode History slice to bytes. 80 func (e *JSONPBEncoder) EncodeHistories(histories []*historypb.History) ([]byte, error) { 81 return e.encodeSlice( 82 len(histories), 83 func(i int) proto.Message { return histories[i] }) 84 } 85 86 // Decode HistoryEvent slice from bytes. 87 func (e *JSONPBEncoder) DecodeHistoryEvents(data []byte) ([]*historypb.HistoryEvent, error) { 88 var historyEvents []*historypb.HistoryEvent 89 err := e.DecodeSlice( 90 data, 91 func() proto.Message { 92 historyEvent := &historypb.HistoryEvent{} 93 historyEvents = append(historyEvents, historyEvent) 94 return historyEvent 95 }) 96 return historyEvents, err 97 } 98 99 // Decode History slice from bytes. 100 func (e *JSONPBEncoder) DecodeHistories(data []byte) ([]*historypb.History, error) { 101 var histories []*historypb.History 102 err := e.DecodeSlice( 103 data, 104 func() proto.Message { 105 history := &historypb.History{} 106 histories = append(histories, history) 107 return history 108 }) 109 110 return histories, err 111 } 112 113 // Due to the lack of generics in go 114 // this function accepts callback which should return particular item by it index. 115 func (e *JSONPBEncoder) encodeSlice( 116 len int, 117 item func(i int) proto.Message, 118 ) ([]byte, error) { 119 var buf bytes.Buffer 120 buf.WriteString("[") 121 for i := 0; i < len; i++ { 122 pb := item(i) 123 bs, err := e.marshaler.Marshal(pb) 124 if err != nil { 125 return nil, err 126 } 127 buf.Write(bs) 128 129 if i == len-1 { 130 buf.WriteString("]") 131 } else { 132 buf.WriteString(",") 133 } 134 } 135 return buf.Bytes(), nil 136 } 137 138 // constructor callback must create empty object, add it to result slice, and return it. 139 func (e *JSONPBEncoder) DecodeSlice( 140 data []byte, 141 constructor func() proto.Message) error { 142 143 dec := json.NewDecoder(bytes.NewReader(data)) 144 145 tok, err := dec.Token() 146 if err != nil { 147 return err 148 } 149 if delim, ok := tok.(json.Delim); !ok || delim != '[' { 150 return fmt.Errorf("invalid json: expected [ but found %v", tok) 151 } 152 153 // We need DiscardUnknown here as the history json may have been written by a 154 // different proto revision 155 unmarshaller := temporalproto.CustomJSONUnmarshalOptions{ 156 DiscardUnknown: true, 157 } 158 159 var buf json.RawMessage 160 for dec.More() { 161 if err := dec.Decode(&buf); err != nil { 162 return err 163 } 164 pb := constructor() 165 if err := unmarshaller.Unmarshal([]byte(buf), pb); err != nil { 166 return err 167 } 168 buf = buf[:0] 169 } 170 171 return nil 172 }