github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/yaml/decoder.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package yaml 18 19 import ( 20 "bufio" 21 "bytes" 22 "encoding/json" 23 "fmt" 24 "io" 25 "io/ioutil" 26 "strings" 27 "unicode" 28 29 "github.com/ghodss/yaml" 30 "github.com/golang/glog" 31 ) 32 33 // ToJSON converts a single YAML document into a JSON document 34 // or returns an error. If the document appears to be JSON the 35 // YAML decoding path is not used (so that error messages are 36 // JSON specific). 37 func ToJSON(data []byte) ([]byte, error) { 38 if hasJSONPrefix(data) { 39 return data, nil 40 } 41 return yaml.YAMLToJSON(data) 42 } 43 44 // YAMLToJSONDecoder decodes YAML documents from an io.Reader by 45 // separating individual documents. It first converts the YAML 46 // body to JSON, then unmarshals the JSON. 47 type YAMLToJSONDecoder struct { 48 scanner *bufio.Scanner 49 } 50 51 // NewYAMLToJSONDecoder decodes YAML documents from the provided 52 // stream in chunks by converting each document (as defined by 53 // the YAML spec) into its own chunk, converting it to JSON via 54 // yaml.YAMLToJSON, and then passing it to json.Decoder. 55 func NewYAMLToJSONDecoder(r io.Reader) *YAMLToJSONDecoder { 56 scanner := bufio.NewScanner(r) 57 scanner.Split(splitYAMLDocument) 58 return &YAMLToJSONDecoder{ 59 scanner: scanner, 60 } 61 } 62 63 // Decode reads a YAML document as JSON from the stream or returns 64 // an error. The decoding rules match json.Unmarshal, not 65 // yaml.Unmarshal. 66 func (d *YAMLToJSONDecoder) Decode(into interface{}) error { 67 if d.scanner.Scan() { 68 data, err := yaml.YAMLToJSON(d.scanner.Bytes()) 69 if err != nil { 70 return err 71 } 72 return json.Unmarshal(data, into) 73 } 74 err := d.scanner.Err() 75 if err == nil { 76 err = io.EOF 77 } 78 return err 79 } 80 81 // YAMLDecoder reads chunks of objects and returns ErrShortBuffer if 82 // the data is not sufficient. 83 type YAMLDecoder struct { 84 r io.ReadCloser 85 scanner *bufio.Scanner 86 remaining []byte 87 } 88 89 // NewDocumentDecoder decodes YAML documents from the provided 90 // stream in chunks by converting each document (as defined by 91 // the YAML spec) into its own chunk. io.ErrShortBuffer will be 92 // returned if the entire buffer could not be read to assist 93 // the caller in framing the chunk. 94 func NewDocumentDecoder(r io.ReadCloser) io.ReadCloser { 95 scanner := bufio.NewScanner(r) 96 scanner.Split(splitYAMLDocument) 97 return &YAMLDecoder{ 98 r: r, 99 scanner: scanner, 100 } 101 } 102 103 // Read reads the previous slice into the buffer, or attempts to read 104 // the next chunk. 105 // TODO: switch to readline approach. 106 func (d *YAMLDecoder) Read(data []byte) (n int, err error) { 107 left := len(d.remaining) 108 if left == 0 { 109 // return the next chunk from the stream 110 if !d.scanner.Scan() { 111 err := d.scanner.Err() 112 if err == nil { 113 err = io.EOF 114 } 115 return 0, err 116 } 117 out := d.scanner.Bytes() 118 d.remaining = out 119 left = len(out) 120 } 121 122 // fits within data 123 if left <= len(data) { 124 copy(data, d.remaining) 125 d.remaining = nil 126 return len(d.remaining), nil 127 } 128 129 // caller will need to reread 130 copy(data, d.remaining[:left]) 131 d.remaining = d.remaining[left:] 132 return len(data), io.ErrShortBuffer 133 } 134 135 func (d *YAMLDecoder) Close() error { 136 return d.r.Close() 137 } 138 139 const yamlSeparator = "\n---" 140 141 // splitYAMLDocument is a bufio.SplitFunc for splitting YAML streams into individual documents. 142 func splitYAMLDocument(data []byte, atEOF bool) (advance int, token []byte, err error) { 143 if atEOF && len(data) == 0 { 144 return 0, nil, nil 145 } 146 sep := len([]byte(yamlSeparator)) 147 if i := bytes.Index(data, []byte(yamlSeparator)); i >= 0 { 148 // We have a potential document terminator 149 i += sep 150 after := data[i:] 151 if len(after) == 0 { 152 // we can't read any more characters 153 if atEOF { 154 return len(data), data[:len(data)-sep], nil 155 } 156 return 0, nil, nil 157 } 158 if j := bytes.IndexByte(after, '\n'); j >= 0 { 159 return i + j + 1, data[0 : i-sep], nil 160 } 161 return 0, nil, nil 162 } 163 // If we're at EOF, we have a final, non-terminated line. Return it. 164 if atEOF { 165 return len(data), data, nil 166 } 167 // Request more data. 168 return 0, nil, nil 169 } 170 171 // decoder is a convenience interface for Decode. 172 type decoder interface { 173 Decode(into interface{}) error 174 } 175 176 // YAMLOrJSONDecoder attempts to decode a stream of JSON documents or 177 // YAML documents by sniffing for a leading { character. 178 type YAMLOrJSONDecoder struct { 179 r io.Reader 180 bufferSize int 181 182 decoder decoder 183 } 184 185 // NewYAMLOrJSONDecoder returns a decoder that will process YAML documents 186 // or JSON documents from the given reader as a stream. bufferSize determines 187 // how far into the stream the decoder will look to figure out whether this 188 // is a JSON stream (has whitespace followed by an open brace). 189 func NewYAMLOrJSONDecoder(r io.Reader, bufferSize int) *YAMLOrJSONDecoder { 190 return &YAMLOrJSONDecoder{ 191 r: r, 192 bufferSize: bufferSize, 193 } 194 } 195 196 // Decode unmarshals the next object from the underlying stream into the 197 // provide object, or returns an error. 198 func (d *YAMLOrJSONDecoder) Decode(into interface{}) error { 199 if d.decoder == nil { 200 buffer, isJSON := GuessJSONStream(d.r, d.bufferSize) 201 if isJSON { 202 glog.V(4).Infof("decoding stream as JSON") 203 d.decoder = json.NewDecoder(buffer) 204 } else { 205 glog.V(4).Infof("decoding stream as YAML") 206 d.decoder = NewYAMLToJSONDecoder(buffer) 207 } 208 } 209 err := d.decoder.Decode(into) 210 if jsonDecoder, ok := d.decoder.(*json.Decoder); ok { 211 if syntax, ok := err.(*json.SyntaxError); ok { 212 data, readErr := ioutil.ReadAll(jsonDecoder.Buffered()) 213 if readErr != nil { 214 glog.V(4).Infof("reading stream failed: %v", readErr) 215 } 216 js := string(data) 217 start := strings.LastIndex(js[:syntax.Offset], "\n") + 1 218 line := strings.Count(js[:start], "\n") 219 return fmt.Errorf("json: line %d: %s", line, syntax.Error()) 220 } 221 } 222 return err 223 } 224 225 // GuessJSONStream scans the provided reader up to size, looking 226 // for an open brace indicating this is JSON. It will return the 227 // bufio.Reader it creates for the consumer. 228 func GuessJSONStream(r io.Reader, size int) (io.Reader, bool) { 229 buffer := bufio.NewReaderSize(r, size) 230 b, _ := buffer.Peek(size) 231 return buffer, hasJSONPrefix(b) 232 } 233 234 var jsonPrefix = []byte("{") 235 236 // hasJSONPrefix returns true if the provided buffer appears to start with 237 // a JSON open brace. 238 func hasJSONPrefix(buf []byte) bool { 239 return hasPrefix(buf, jsonPrefix) 240 } 241 242 // Return true if the first non-whitespace bytes in buf is 243 // prefix. 244 func hasPrefix(buf []byte, prefix []byte) bool { 245 trim := bytes.TrimLeftFunc(buf, unicode.IsSpace) 246 return bytes.HasPrefix(trim, prefix) 247 }