1 /* 2 Copyright 2021 The Kubernetes Authors. 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 json 18 19 import ( 20 gojson "encoding/json" 21 "fmt" 22 "io" 23 24 internaljson "sigs.k8s.io/json/internal/golang/encoding/json" 25 ) 26 27 // Decoder describes the decoding API exposed by `encoding/json#Decoder` 28 type Decoder interface { 29 Decode(v interface{}) error 30 Buffered() io.Reader 31 Token() (gojson.Token, error) 32 More() bool 33 InputOffset() int64 34 } 35 36 // NewDecoderCaseSensitivePreserveInts returns a decoder that matches the behavior of encoding/json#NewDecoder, with the following changes: 37 // - When unmarshaling into a struct, JSON keys must case-sensitively match `json` tag names (for tagged struct fields) 38 // or struct field names (for untagged struct fields), or they are treated as unknown fields and discarded. 39 // - When unmarshaling a number into an interface value, it is unmarshaled as an int64 if 40 // the JSON data does not contain a "." character and parses as an integer successfully and 41 // does not overflow int64. Otherwise, the number is unmarshaled as a float64. 42 // - If a syntax error is returned, it will not be of type encoding/json#SyntaxError, 43 // but will be recognizeable by this package's IsSyntaxError() function. 44 func NewDecoderCaseSensitivePreserveInts(r io.Reader) Decoder { 45 d := internaljson.NewDecoder(r) 46 d.CaseSensitive() 47 d.PreserveInts() 48 return d 49 } 50 51 // UnmarshalCaseSensitivePreserveInts parses the JSON-encoded data and stores the result in the value pointed to by v. 52 // 53 // UnmarshalCaseSensitivePreserveInts matches the behavior of encoding/json#Unmarshal, with the following changes: 54 // - When unmarshaling into a struct, JSON keys must case-sensitively match `json` tag names (for tagged struct fields) 55 // or struct field names (for untagged struct fields), or they are treated as unknown fields and discarded. 56 // - When unmarshaling a number into an interface value, it is unmarshaled as an int64 if 57 // the JSON data does not contain a "." character and parses as an integer successfully and 58 // does not overflow int64. Otherwise, the number is unmarshaled as a float64. 59 // - If a syntax error is returned, it will not be of type encoding/json#SyntaxError, 60 // but will be recognizeable by this package's IsSyntaxError() function. 61 func UnmarshalCaseSensitivePreserveInts(data []byte, v interface{}) error { 62 return internaljson.Unmarshal( 63 data, 64 v, 65 internaljson.CaseSensitive, 66 internaljson.PreserveInts, 67 ) 68 } 69 70 type StrictOption int 71 72 const ( 73 // DisallowDuplicateFields returns strict errors if data contains duplicate fields 74 DisallowDuplicateFields StrictOption = 1 75 76 // DisallowUnknownFields returns strict errors if data contains unknown fields when decoding into typed structs 77 DisallowUnknownFields StrictOption = 2 78 ) 79 80 // UnmarshalStrict parses the JSON-encoded data and stores the result in the value pointed to by v. 81 // Unmarshaling is performed identically to UnmarshalCaseSensitivePreserveInts(), returning an error on failure. 82 // 83 // If parsing succeeds, additional strict checks as selected by `strictOptions` are performed 84 // and a list of the strict failures (if any) are returned. If no `strictOptions` are selected, 85 // all supported strict checks are performed. 86 // 87 // Strict errors returned will implement the FieldError interface for the specific erroneous fields. 88 // 89 // Currently supported strict checks are: 90 // - DisallowDuplicateFields: ensure the data contains no duplicate fields 91 // - DisallowUnknownFields: ensure the data contains no unknown fields (when decoding into typed structs) 92 // 93 // Additional strict checks may be added in the future. 94 // 95 // Note that the strict checks do not change what is stored in v. 96 // For example, if duplicate fields are present, they will be parsed and stored in v, 97 // and errors about the duplicate fields will be returned in the strict error list. 98 func UnmarshalStrict(data []byte, v interface{}, strictOptions ...StrictOption) (strictErrors []error, err error) { 99 if len(strictOptions) == 0 { 100 err = internaljson.Unmarshal(data, v, 101 // options matching UnmarshalCaseSensitivePreserveInts 102 internaljson.CaseSensitive, 103 internaljson.PreserveInts, 104 // all strict options 105 internaljson.DisallowDuplicateFields, 106 internaljson.DisallowUnknownFields, 107 ) 108 } else { 109 opts := make([]internaljson.UnmarshalOpt, 0, 2+len(strictOptions)) 110 // options matching UnmarshalCaseSensitivePreserveInts 111 opts = append(opts, internaljson.CaseSensitive, internaljson.PreserveInts) 112 for _, strictOpt := range strictOptions { 113 switch strictOpt { 114 case DisallowDuplicateFields: 115 opts = append(opts, internaljson.DisallowDuplicateFields) 116 case DisallowUnknownFields: 117 opts = append(opts, internaljson.DisallowUnknownFields) 118 default: 119 return nil, fmt.Errorf("unknown strict option %d", strictOpt) 120 } 121 } 122 err = internaljson.Unmarshal(data, v, opts...) 123 } 124 125 if strictErr, ok := err.(*internaljson.UnmarshalStrictError); ok { 126 return strictErr.Errors, nil 127 } 128 return nil, err 129 } 130 131 // SyntaxErrorOffset returns if the specified error is a syntax error produced by encoding/json or this package. 132 func SyntaxErrorOffset(err error) (isSyntaxError bool, offset int64) { 133 switch err := err.(type) { 134 case *gojson.SyntaxError: 135 return true, err.Offset 136 case *internaljson.SyntaxError: 137 return true, err.Offset 138 default: 139 return false, 0 140 } 141 } 142 143 // FieldError is an error that provides access to the path of the erroneous field 144 type FieldError interface { 145 error 146 // FieldPath provides the full path of the erroneous field within the json object. 147 FieldPath() string 148 // SetFieldPath updates the path of the erroneous field output in the error message. 149 SetFieldPath(path string) 150 } 151