blob: 764e2a84c728cbc4deed0fcef9ccfb8d0b2170e3 [file] [log] [blame]
Abhay Kumara61c5222025-11-10 07:32:50 +00001/*
2Copyright 2021 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package json
18
19import (
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`
28type 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.
44func 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.
61func UnmarshalCaseSensitivePreserveInts(data []byte, v interface{}) error {
62 return internaljson.Unmarshal(
63 data,
64 v,
65 internaljson.CaseSensitive,
66 internaljson.PreserveInts,
67 )
68}
69
70type StrictOption int
71
72const (
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// Currently supported strict checks are:
88// - DisallowDuplicateFields: ensure the data contains no duplicate fields
89// - DisallowUnknownFields: ensure the data contains no unknown fields (when decoding into typed structs)
90//
91// Additional strict checks may be added in the future.
92//
93// Note that the strict checks do not change what is stored in v.
94// For example, if duplicate fields are present, they will be parsed and stored in v,
95// and errors about the duplicate fields will be returned in the strict error list.
96func UnmarshalStrict(data []byte, v interface{}, strictOptions ...StrictOption) (strictErrors []error, err error) {
97 if len(strictOptions) == 0 {
98 err = internaljson.Unmarshal(data, v,
99 // options matching UnmarshalCaseSensitivePreserveInts
100 internaljson.CaseSensitive,
101 internaljson.PreserveInts,
102 // all strict options
103 internaljson.DisallowDuplicateFields,
104 internaljson.DisallowUnknownFields,
105 )
106 } else {
107 opts := make([]internaljson.UnmarshalOpt, 0, 2+len(strictOptions))
108 // options matching UnmarshalCaseSensitivePreserveInts
109 opts = append(opts, internaljson.CaseSensitive, internaljson.PreserveInts)
110 for _, strictOpt := range strictOptions {
111 switch strictOpt {
112 case DisallowDuplicateFields:
113 opts = append(opts, internaljson.DisallowDuplicateFields)
114 case DisallowUnknownFields:
115 opts = append(opts, internaljson.DisallowUnknownFields)
116 default:
117 return nil, fmt.Errorf("unknown strict option %d", strictOpt)
118 }
119 }
120 err = internaljson.Unmarshal(data, v, opts...)
121 }
122
123 if strictErr, ok := err.(*internaljson.UnmarshalStrictError); ok {
124 return strictErr.Errors, nil
125 }
126 return nil, err
127}
128
129// SyntaxErrorOffset returns if the specified error is a syntax error produced by encoding/json or this package.
130func SyntaxErrorOffset(err error) (isSyntaxError bool, offset int64) {
131 switch err := err.(type) {
132 case *gojson.SyntaxError:
133 return true, err.Offset
134 case *internaljson.SyntaxError:
135 return true, err.Offset
136 default:
137 return false, 0
138 }
139}