Skip to content

Commit

Permalink
Use a decoder instead of Unmarshall for JSON parsing.
Browse files Browse the repository at this point in the history
It allows to use UseNumber() and get json.Number instead of a float64
primitive.
  • Loading branch information
sigu-399 committed Sep 21, 2015
1 parent 888f4e6 commit ce63822
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 240 deletions.
19 changes: 13 additions & 6 deletions jsonLoader.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
package gojsonschema

import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
Expand Down Expand Up @@ -150,7 +151,9 @@ func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error)
}

var document interface{}
err = json.Unmarshal(bodyBuff, &document)
decoder := json.NewDecoder(bytes.NewReader(bodyBuff))
decoder.UseNumber()
err = decoder.Decode(&document)
if err != nil {
return nil, err
}
Expand All @@ -166,7 +169,9 @@ func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
}

var document interface{}
err = json.Unmarshal(bodyBuff, &document)
decoder := json.NewDecoder(bytes.NewReader(bodyBuff))
decoder.UseNumber()
err = decoder.Decode(&document)
if err != nil {
return nil, err
}
Expand All @@ -191,8 +196,9 @@ func NewStringLoader(source string) *jsonStringLoader {
func (l *jsonStringLoader) loadJSON() (interface{}, error) {

var document interface{}

err := json.Unmarshal([]byte(l.jsonSource().(string)), &document)
decoder := json.NewDecoder(strings.NewReader(l.jsonSource().(string)))
decoder.UseNumber()
err := decoder.Decode(&document)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -253,8 +259,9 @@ func (l *jsonGoLoader) loadJSON() (interface{}, error) {
}

var document interface{}

err = json.Unmarshal(jsonBytes, &document)
decoder := json.NewDecoder(bytes.NewReader(jsonBytes))
decoder.UseNumber()
err = decoder.Decode(&document)
if err != nil {
return nil, err
}
Expand Down
12 changes: 6 additions & 6 deletions subSchema.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,23 +105,23 @@ type subSchema struct {
exclusiveMinimum bool

// validation : string
minLength *int
maxLength *int
minLength *int64
maxLength *int64
pattern *regexp.Regexp
format string

// validation : object
minProperties *int
maxProperties *int
minProperties *int64
maxProperties *int64
required []string

dependencies map[string]interface{}
additionalProperties interface{}
patternProperties map[string]*subSchema

// validation : array
minItems *int
maxItems *int
minItems *int64
maxItems *int64
uniqueItems bool

additionalItems interface{}
Expand Down
56 changes: 35 additions & 21 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ import (
"reflect"
)

func isJsonNumber(what interface{}) bool {
switch what.(type) {

case json.Number:
return true
}

return false
}

func isKind(what interface{}, kind reflect.Kind) bool {
return reflect.ValueOf(what).Kind() == kind
}
Expand Down Expand Up @@ -77,49 +87,53 @@ func isFloat64AnInteger(f float64) bool {
return f == float64(int64(f)) || f == float64(uint64(f))
}

func mustBeInteger(what interface{}) *int {

var number int
func mustBeInteger(what interface{}) *int64 {

if isKind(what, reflect.Float64) {
if isJsonNumber(what) {

fnumber := what.(float64)
number := what.(json.Number)
int64Value, err := number.Int64()

if isFloat64AnInteger(fnumber) {
number = int(fnumber)
return &number
if err == nil {
return &int64Value
} else {
return nil
}

} else if isKind(what, reflect.Int) {

number = what.(int)
return &number

}

return nil
}

func mustBeNumber(what interface{}) *float64 {

var number float64
if isJsonNumber(what) {

if isKind(what, reflect.Float64) {
number := what.(json.Number)
float64Value, err := number.Float64()

number = what.(float64)
return &number
if err == nil {
return &float64Value
} else {
return nil
}

} else if isKind(what, reflect.Int) {
}

number = float64(what.(int))
return &number
return nil

}

// formats a number so that it is displayed as the smallest string possible
func resultErrorFormatJsonNumber(n json.Number) string {

if int64Value, err := n.Int64(); err == nil {
return fmt.Sprintf("%d", int64Value)
}

return nil
float64Value, _ := n.Float64()

return fmt.Sprintf("%g", float64Value)
}

// formats a number so that it is displayed as the smallest string possible
Expand Down
95 changes: 1 addition & 94 deletions utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,101 +31,8 @@ import (
"testing"
)

func TestIsFloat64IntegerA(t *testing.T) {

assert.False(t, isFloat64AnInteger(math.Inf(+1)))
assert.False(t, isFloat64AnInteger(math.Inf(-1)))
assert.False(t, isFloat64AnInteger(math.NaN()))
assert.False(t, isFloat64AnInteger(math.Float64frombits((1<<11-1)<<52|1)))
assert.True(t, isFloat64AnInteger(float64(0.0)))
assert.True(t, isFloat64AnInteger(-float64(0.0)))
assert.False(t, isFloat64AnInteger(float64(0.5)))
assert.True(t, isFloat64AnInteger(float64(1.0)))
assert.True(t, isFloat64AnInteger(-float64(1.0)))
assert.False(t, isFloat64AnInteger(float64(1.1)))
assert.True(t, isFloat64AnInteger(float64(131.0)))
assert.True(t, isFloat64AnInteger(-float64(131.0)))
assert.True(t, isFloat64AnInteger(float64(1<<52-1)))
assert.True(t, isFloat64AnInteger(-float64(1<<52-1)))
assert.True(t, isFloat64AnInteger(float64(1<<52)))
assert.True(t, isFloat64AnInteger(-float64(1<<52)))
assert.True(t, isFloat64AnInteger(float64(1<<53-1)))
assert.True(t, isFloat64AnInteger(-float64(1<<53-1)))
assert.True(t, isFloat64AnInteger(float64(1<<53-1)))
assert.False(t, isFloat64AnInteger(float64(1<<53)))
assert.False(t, isFloat64AnInteger(-float64(1<<53)))
assert.False(t, isFloat64AnInteger(float64(1<<63)))
assert.False(t, isFloat64AnInteger(-float64(1<<63)))
assert.False(t, isFloat64AnInteger(math.Nextafter(float64(1<<63), math.MaxFloat64)))
assert.False(t, isFloat64AnInteger(-math.Nextafter(float64(1<<63), math.MaxFloat64)))
assert.False(t, isFloat64AnInteger(float64(1<<70+3<<21)))
assert.False(t, isFloat64AnInteger(-float64(1<<70+3<<21)))

assert.False(t, isFloat64AnInteger(math.Nextafter(float64(9007199254740991.0), math.MaxFloat64)))
assert.True(t, isFloat64AnInteger(float64(9007199254740991.0)))

assert.True(t, isFloat64AnInteger(float64(-9007199254740991.0)))
assert.False(t, isFloat64AnInteger(math.Nextafter(float64(-9007199254740991.0), -math.MaxFloat64)))
}

func TestIsFloat64Integer(t *testing.T) {
// fails. MaxUint64 is to large for JS anyway, so we can ignore it here.
//assert.True(t, isFloat64AnInteger(float64(math.MaxUint64)))

assert.False(t, isFloat64AnInteger(math.MaxInt64))
assert.False(t, isFloat64AnInteger(1<<62))
assert.False(t, isFloat64AnInteger(math.MinInt64))
assert.True(t, isFloat64AnInteger(100100100100))
assert.True(t, isFloat64AnInteger(-100100100100))
assert.True(t, isFloat64AnInteger(100100100))
assert.True(t, isFloat64AnInteger(-100100100))
assert.True(t, isFloat64AnInteger(100100))
assert.True(t, isFloat64AnInteger(-100100))
assert.True(t, isFloat64AnInteger(100))
assert.True(t, isFloat64AnInteger(-100))
assert.True(t, isFloat64AnInteger(-0))
assert.True(t, isFloat64AnInteger(-1))
assert.True(t, isFloat64AnInteger(1))
assert.True(t, isFloat64AnInteger(0))
assert.True(t, isFloat64AnInteger(77))
assert.True(t, isFloat64AnInteger(-77))
assert.True(t, isFloat64AnInteger(1e10))
assert.True(t, isFloat64AnInteger(-1e10))
assert.True(t, isFloat64AnInteger(100100100.0))
assert.True(t, isFloat64AnInteger(-100100100.0))

assert.False(t, isFloat64AnInteger(100100100100.1))
assert.False(t, isFloat64AnInteger(-100100100100.1))
assert.False(t, isFloat64AnInteger(math.MaxFloat64))
assert.False(t, isFloat64AnInteger(-math.MaxFloat64))
assert.False(t, isFloat64AnInteger(1.1))
assert.False(t, isFloat64AnInteger(-1.1))
assert.False(t, isFloat64AnInteger(1.000000000001))
assert.False(t, isFloat64AnInteger(-1.000000000001))
assert.False(t, isFloat64AnInteger(1e-10))
assert.False(t, isFloat64AnInteger(-1e-10))

assert.False(t, isFloat64AnInteger(1.001))
assert.False(t, isFloat64AnInteger(-1.001))

assert.False(t, isFloat64AnInteger(0.0001))

assert.False(t, isFloat64AnInteger(1<<62))
assert.False(t, isFloat64AnInteger(math.MinInt64))
assert.False(t, isFloat64AnInteger(math.MaxInt64))
assert.False(t, isFloat64AnInteger(-1<<62))

assert.False(t, isFloat64AnInteger(1e-10))
assert.False(t, isFloat64AnInteger(-1e-10))

assert.False(t, isFloat64AnInteger(1.000000000001))
assert.False(t, isFloat64AnInteger(-1.000000000001))

assert.False(t, isFloat64AnInteger(4.611686018427387904e7))
assert.False(t, isFloat64AnInteger(-4.611686018427387904e7))
}

func TestResultErrorFormatNumber(t *testing.T) {

assert.Equal(t, "1", resultErrorFormatNumber(1))
assert.Equal(t, "-1", resultErrorFormatNumber(-1))
assert.Equal(t, "0", resultErrorFormatNumber(0))
Expand Down
Loading

0 comments on commit ce63822

Please sign in to comment.