...

Source file src/sigs.k8s.io/json/json.go

Documentation: sigs.k8s.io/json

     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  

View as plain text