// If the JSON array is smaller than the Go array,
// the additional Go array elements are set to zero values.
//
-// To unmarshal a JSON object into a string-keyed map, Unmarshal first
+// To unmarshal a JSON object into a string-keyed map, Unmarshal first
// establishes a map to use, If the map is nil, Unmarshal allocates a new map.
// Otherwise Unmarshal reuses the existing map, keeping existing entries.
// Unmarshal then stores key-value pairs from the JSON object into the map.
return strconv.ParseInt(string(n), 10, 64)
}
-// IsValid returns if the number is a valid JSON number literal.
-func (n Number) IsValid() bool {
+// isValidNumber reports whether s is a valid JSON number literal.
+func isValidNumber(s string) bool {
// This function implements the JSON numbers grammar.
// See https://tools.ietf.org/html/rfc7159#section-6
// and http://json.org/number.gif
- l := len(n)
- if l == 0 {
+ if s == "" {
return false
}
- i := 0
- c := n[i]
- i++
-
// Optional -
- if c == '-' {
- if i == l {
+ if s[0] == '-' {
+ s = s[1:]
+ if s == "" {
return false
}
-
- c = n[i]
- i++
}
- // 1-9
- if c >= '1' && c <= '9' {
- // Eat digits.
- for ; i < l; i++ {
- c = n[i]
- if c < '0' || c > '9' {
- break
- }
- }
- i++
- } else if c != '0' {
- // If it's not 0 or 1-9 it's invalid.
+ // Digits
+ switch {
+ default:
return false
- } else {
- if i == l {
- // Just 0
- return true
- }
- // Skip the 0
- c = n[i]
- i++
- }
-
- // . followed by 1 or more digits.
- if c == '.' {
- if i == l {
- // Just 1. is invalid.
- return false
- }
+ case s[0] == '0':
+ s = s[1:]
- // . needs to be followed by at least one digit.
- c = n[i]
- i++
- if c < '0' || c > '9' {
- return false
+ case '1' <= s[0] && s[0] <= '9':
+ s = s[1:]
+ for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
+ s = s[1:]
}
+ }
- // Eat digits.
- for ; i < l; i++ {
- c = n[i]
- if c < '0' || c > '9' {
- break
- }
+ // . followed by 1 or more digits.
+ if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
+ s = s[2:]
+ for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
+ s = s[1:]
}
- i++
}
// e or E followed by an optional - or + and
// 1 or more digits.
- if c == 'e' || c == 'E' {
- if i == l {
- // Just 1e is invalid.
- return false
- }
-
- c = n[i]
- i++
-
- // Optional - or +
- if c == '-' || c == '+' {
- if i == l {
- // Just 1e+ is invalid.
+ if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
+ s = s[1:]
+ if s[0] == '+' || s[0] == '-' {
+ s = s[1:]
+ if s == "" {
return false
}
-
- c = n[i]
- i++
}
-
- // Need to have a digit.
- if c < '0' || c > '9' {
- return false
- }
-
- // Eat digits.
- for ; i < l; i++ {
- c = n[i]
- if c < '0' || c > '9' {
- break
- }
+ for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
+ s = s[1:]
}
- i++
}
// Make sure we are at the end.
- if i <= l {
- return false
- }
-
- return true
+ return s == ""
}
// decodeState represents the state while decoding a JSON value.
default:
if v.Kind() == reflect.String && v.Type() == numberType {
v.SetString(s)
- if !Number(s).IsValid() {
+ if !isValidNumber(s) {
d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item))
}
break
}
for _, test := range validTests {
- if !Number(test).IsValid() {
+ if !isValidNumber(test) {
t.Errorf("%s should be valid", test)
}
var f float64
if err := Unmarshal([]byte(test), &f); err != nil {
- t.Errorf("%s should be invalid: %v", test, err)
+ t.Errorf("%s should be valid but Unmarshal failed: %v", test, err)
}
if !jsonNumberRegexp.MatchString(test) {
- t.Errorf("%s should be invalid", test)
+ t.Errorf("%s should be valid but regexp does not match", test)
}
}
}
for _, test := range invalidTests {
- if Number(test).IsValid() {
+ if isValidNumber(test) {
t.Errorf("%s should be invalid", test)
}
var f float64
if err := Unmarshal([]byte(test), &f); err == nil {
- t.Errorf("%s should be valid: %v", test, f)
+ t.Errorf("%s should be invalid but unmarshal wrote %v", test, f)
}
if jsonNumberRegexp.MatchString(test) {
- t.Errorf("%s should be valid", test)
+ t.Errorf("%s should be invalid but matches regexp", test)
}
}
}
func BenchmarkNumberIsValid(b *testing.B) {
- n := Number("-61657.61667E+61673")
+ s := "-61657.61667E+61673"
for i := 0; i < b.N; i++ {
- n.IsValid()
+ isValidNumber(s)
}
}
func BenchmarkNumberIsValidRegexp(b *testing.B) {
var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`)
- n := "-61657.61667E+61673"
+ s := "-61657.61667E+61673"
for i := 0; i < b.N; i++ {
- jsonNumberRegexp.MatchString(n)
+ jsonNumberRegexp.MatchString(s)
}
}