209 lines
4.1 KiB
Go
209 lines
4.1 KiB
Go
|
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
// author xeipuuv
|
||
|
// author-github https://github.com/xeipuuv
|
||
|
// author-mail xeipuuv@gmail.com
|
||
|
//
|
||
|
// repository-name gojsonschema
|
||
|
// repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
|
||
|
//
|
||
|
// description Various utility functions.
|
||
|
//
|
||
|
// created 26-02-2013
|
||
|
|
||
|
package gojsonschema
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"reflect"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
func isKind(what interface{}, kind reflect.Kind) bool {
|
||
|
target := what
|
||
|
if isJsonNumber(what) {
|
||
|
// JSON Numbers are strings!
|
||
|
target = *mustBeNumber(what)
|
||
|
}
|
||
|
return reflect.ValueOf(target).Kind() == kind
|
||
|
}
|
||
|
|
||
|
func existsMapKey(m map[string]interface{}, k string) bool {
|
||
|
_, ok := m[k]
|
||
|
return ok
|
||
|
}
|
||
|
|
||
|
func isStringInSlice(s []string, what string) bool {
|
||
|
for i := range s {
|
||
|
if s[i] == what {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func marshalToJsonString(value interface{}) (*string, error) {
|
||
|
|
||
|
mBytes, err := json.Marshal(value)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
sBytes := string(mBytes)
|
||
|
return &sBytes, nil
|
||
|
}
|
||
|
|
||
|
func isJsonNumber(what interface{}) bool {
|
||
|
|
||
|
switch what.(type) {
|
||
|
|
||
|
case json.Number:
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func checkJsonNumber(what interface{}) (isValidFloat64 bool, isValidInt64 bool, isValidInt32 bool) {
|
||
|
|
||
|
jsonNumber := what.(json.Number)
|
||
|
|
||
|
f64, errFloat64 := jsonNumber.Float64()
|
||
|
s64 := strconv.FormatFloat(f64, 'f', -1, 64)
|
||
|
_, errInt64 := strconv.ParseInt(s64, 10, 64)
|
||
|
|
||
|
isValidFloat64 = errFloat64 == nil
|
||
|
isValidInt64 = errInt64 == nil
|
||
|
|
||
|
_, errInt32 := strconv.ParseInt(s64, 10, 32)
|
||
|
isValidInt32 = isValidInt64 && errInt32 == nil
|
||
|
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
|
||
|
const (
|
||
|
max_json_float = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
|
||
|
min_json_float = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
|
||
|
)
|
||
|
|
||
|
func isFloat64AnInteger(f float64) bool {
|
||
|
|
||
|
if math.IsNaN(f) || math.IsInf(f, 0) || f < min_json_float || f > max_json_float {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return f == float64(int64(f)) || f == float64(uint64(f))
|
||
|
}
|
||
|
|
||
|
func mustBeInteger(what interface{}) *int {
|
||
|
|
||
|
if isJsonNumber(what) {
|
||
|
|
||
|
number := what.(json.Number)
|
||
|
|
||
|
_, _, isValidInt32 := checkJsonNumber(number)
|
||
|
|
||
|
if isValidInt32 {
|
||
|
|
||
|
int64Value, err := number.Int64()
|
||
|
if err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
int32Value := int(int64Value)
|
||
|
return &int32Value
|
||
|
|
||
|
} else {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func mustBeNumber(what interface{}) *float64 {
|
||
|
|
||
|
if isJsonNumber(what) {
|
||
|
|
||
|
number := what.(json.Number)
|
||
|
float64Value, err := number.Float64()
|
||
|
|
||
|
if err == nil {
|
||
|
return &float64Value
|
||
|
} else {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
float64Value, _ := n.Float64()
|
||
|
|
||
|
return fmt.Sprintf("%g", float64Value)
|
||
|
}
|
||
|
|
||
|
// formats a number so that it is displayed as the smallest string possible
|
||
|
func resultErrorFormatNumber(n float64) string {
|
||
|
|
||
|
if isFloat64AnInteger(n) {
|
||
|
return fmt.Sprintf("%d", int64(n))
|
||
|
}
|
||
|
|
||
|
return fmt.Sprintf("%g", n)
|
||
|
}
|
||
|
|
||
|
func convertDocumentNode(val interface{}) interface{} {
|
||
|
|
||
|
if lval, ok := val.([]interface{}); ok {
|
||
|
|
||
|
res := []interface{}{}
|
||
|
for _, v := range lval {
|
||
|
res = append(res, convertDocumentNode(v))
|
||
|
}
|
||
|
|
||
|
return res
|
||
|
|
||
|
}
|
||
|
|
||
|
if mval, ok := val.(map[interface{}]interface{}); ok {
|
||
|
|
||
|
res := map[string]interface{}{}
|
||
|
|
||
|
for k, v := range mval {
|
||
|
res[k.(string)] = convertDocumentNode(v)
|
||
|
}
|
||
|
|
||
|
return res
|
||
|
|
||
|
}
|
||
|
|
||
|
return val
|
||
|
}
|