1// Copyright 2017 Istio Authors 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 15package gogoprotomarshal 16 17import ( 18 "encoding/json" 19 "errors" 20 "strings" 21 22 "github.com/ghodss/yaml" 23 "github.com/gogo/protobuf/jsonpb" 24 "github.com/gogo/protobuf/proto" 25 26 "istio.io/pkg/log" 27) 28 29// ToJSON marshals a proto to canonical JSON 30func ToJSON(msg proto.Message) (string, error) { 31 return ToJSONWithIndent(msg, "") 32} 33 34// ToJSONWithIndent marshals a proto to canonical JSON with pretty printed string 35func ToJSONWithIndent(msg proto.Message, indent string) (string, error) { 36 if msg == nil { 37 return "", errors.New("unexpected nil message") 38 } 39 40 // Marshal from proto to json bytes 41 m := jsonpb.Marshaler{Indent: indent} 42 return m.MarshalToString(msg) 43} 44 45// ToYAML marshals a proto to canonical YAML 46func ToYAML(msg proto.Message) (string, error) { 47 js, err := ToJSON(msg) 48 if err != nil { 49 return "", err 50 } 51 yml, err := yaml.JSONToYAML([]byte(js)) 52 return string(yml), err 53} 54 55// ToJSONMap converts a proto message to a generic map using canonical JSON encoding 56// JSON encoding is specified here: https://developers.google.com/protocol-buffers/docs/proto3#json 57func ToJSONMap(msg proto.Message) (map[string]interface{}, error) { 58 js, err := ToJSON(msg) 59 if err != nil { 60 return nil, err 61 } 62 63 // Unmarshal from json bytes to go map 64 var data map[string]interface{} 65 err = json.Unmarshal([]byte(js), &data) 66 if err != nil { 67 return nil, err 68 } 69 70 return data, nil 71} 72 73// ApplyJSON unmarshals a JSON string into a proto message. Unknown fields are allowed 74func ApplyJSON(js string, pb proto.Message) error { 75 reader := strings.NewReader(js) 76 m := jsonpb.Unmarshaler{} 77 if err := m.Unmarshal(reader, pb); err != nil { 78 log.Debugf("Failed to decode proto: %q. Trying decode with AllowUnknownFields=true", err) 79 m.AllowUnknownFields = true 80 reader.Reset(js) 81 return m.Unmarshal(reader, pb) 82 } 83 return nil 84} 85 86// ApplyJSONStrict unmarshals a JSON string into a proto message. 87func ApplyJSONStrict(js string, pb proto.Message) error { 88 reader := strings.NewReader(js) 89 m := jsonpb.Unmarshaler{} 90 return m.Unmarshal(reader, pb) 91} 92 93// ApplyYAML unmarshals a YAML string into a proto message. 94// Unknown fields are allowed. 95func ApplyYAML(yml string, pb proto.Message) error { 96 js, err := yaml.YAMLToJSON([]byte(yml)) 97 if err != nil { 98 return err 99 } 100 return ApplyJSON(string(js), pb) 101} 102 103// ApplyYAML unmarshals a YAML string into a proto message. 104// Unknown fields are notallowed. 105func ApplyYAMLStrict(yml string, pb proto.Message) error { 106 js, err := yaml.YAMLToJSON([]byte(yml)) 107 if err != nil { 108 return err 109 } 110 return ApplyJSONStrict(string(js), pb) 111} 112