json.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /*
  2. Copyright 2021 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package json
  14. import (
  15. gojson "encoding/json"
  16. "fmt"
  17. "io"
  18. internaljson "sigs.k8s.io/json/internal/golang/encoding/json"
  19. )
  20. // Decoder describes the decoding API exposed by `encoding/json#Decoder`
  21. type Decoder interface {
  22. Decode(v interface{}) error
  23. Buffered() io.Reader
  24. Token() (gojson.Token, error)
  25. More() bool
  26. InputOffset() int64
  27. }
  28. // NewDecoderCaseSensitivePreserveInts returns a decoder that matches the behavior of encoding/json#NewDecoder, with the following changes:
  29. // - When unmarshaling into a struct, JSON keys must case-sensitively match `json` tag names (for tagged struct fields)
  30. // or struct field names (for untagged struct fields), or they are treated as unknown fields and discarded.
  31. // - When unmarshaling a number into an interface value, it is unmarshaled as an int64 if
  32. // the JSON data does not contain a "." character and parses as an integer successfully and
  33. // does not overflow int64. Otherwise, the number is unmarshaled as a float64.
  34. // - If a syntax error is returned, it will not be of type encoding/json#SyntaxError,
  35. // but will be recognizeable by this package's IsSyntaxError() function.
  36. func NewDecoderCaseSensitivePreserveInts(r io.Reader) Decoder {
  37. d := internaljson.NewDecoder(r)
  38. d.CaseSensitive()
  39. d.PreserveInts()
  40. return d
  41. }
  42. // UnmarshalCaseSensitivePreserveInts parses the JSON-encoded data and stores the result in the value pointed to by v.
  43. //
  44. // UnmarshalCaseSensitivePreserveInts matches the behavior of encoding/json#Unmarshal, with the following changes:
  45. // - When unmarshaling into a struct, JSON keys must case-sensitively match `json` tag names (for tagged struct fields)
  46. // or struct field names (for untagged struct fields), or they are treated as unknown fields and discarded.
  47. // - When unmarshaling a number into an interface value, it is unmarshaled as an int64 if
  48. // the JSON data does not contain a "." character and parses as an integer successfully and
  49. // does not overflow int64. Otherwise, the number is unmarshaled as a float64.
  50. // - If a syntax error is returned, it will not be of type encoding/json#SyntaxError,
  51. // but will be recognizeable by this package's IsSyntaxError() function.
  52. func UnmarshalCaseSensitivePreserveInts(data []byte, v interface{}) error {
  53. return internaljson.Unmarshal(
  54. data,
  55. v,
  56. internaljson.CaseSensitive,
  57. internaljson.PreserveInts,
  58. )
  59. }
  60. type StrictOption int
  61. const (
  62. // DisallowDuplicateFields returns strict errors if data contains duplicate fields
  63. DisallowDuplicateFields StrictOption = 1
  64. // DisallowUnknownFields returns strict errors if data contains unknown fields when decoding into typed structs
  65. DisallowUnknownFields StrictOption = 2
  66. )
  67. // UnmarshalStrict parses the JSON-encoded data and stores the result in the value pointed to by v.
  68. // Unmarshaling is performed identically to UnmarshalCaseSensitivePreserveInts(), returning an error on failure.
  69. //
  70. // If parsing succeeds, additional strict checks as selected by `strictOptions` are performed
  71. // and a list of the strict failures (if any) are returned. If no `strictOptions` are selected,
  72. // all supported strict checks are performed.
  73. //
  74. // Currently supported strict checks are:
  75. // - DisallowDuplicateFields: ensure the data contains no duplicate fields
  76. // - DisallowUnknownFields: ensure the data contains no unknown fields (when decoding into typed structs)
  77. //
  78. // Additional strict checks may be added in the future.
  79. //
  80. // Note that the strict checks do not change what is stored in v.
  81. // For example, if duplicate fields are present, they will be parsed and stored in v,
  82. // and errors about the duplicate fields will be returned in the strict error list.
  83. func UnmarshalStrict(data []byte, v interface{}, strictOptions ...StrictOption) (strictErrors []error, err error) {
  84. if len(strictOptions) == 0 {
  85. err = internaljson.Unmarshal(data, v,
  86. // options matching UnmarshalCaseSensitivePreserveInts
  87. internaljson.CaseSensitive,
  88. internaljson.PreserveInts,
  89. // all strict options
  90. internaljson.DisallowDuplicateFields,
  91. internaljson.DisallowUnknownFields,
  92. )
  93. } else {
  94. opts := make([]internaljson.UnmarshalOpt, 0, 2+len(strictOptions))
  95. // options matching UnmarshalCaseSensitivePreserveInts
  96. opts = append(opts, internaljson.CaseSensitive, internaljson.PreserveInts)
  97. for _, strictOpt := range strictOptions {
  98. switch strictOpt {
  99. case DisallowDuplicateFields:
  100. opts = append(opts, internaljson.DisallowDuplicateFields)
  101. case DisallowUnknownFields:
  102. opts = append(opts, internaljson.DisallowUnknownFields)
  103. default:
  104. return nil, fmt.Errorf("unknown strict option %d", strictOpt)
  105. }
  106. }
  107. err = internaljson.Unmarshal(data, v, opts...)
  108. }
  109. if strictErr, ok := err.(*internaljson.UnmarshalStrictError); ok {
  110. return strictErr.Errors, nil
  111. }
  112. return nil, err
  113. }
  114. // SyntaxErrorOffset returns if the specified error is a syntax error produced by encoding/json or this package.
  115. func SyntaxErrorOffset(err error) (isSyntaxError bool, offset int64) {
  116. switch err := err.(type) {
  117. case *gojson.SyntaxError:
  118. return true, err.Offset
  119. case *internaljson.SyntaxError:
  120. return true, err.Offset
  121. default:
  122. return false, 0
  123. }
  124. }