198 lines
4.1 KiB
Go
198 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"
|
|
"math/big"
|
|
"reflect"
|
|
)
|
|
|
|
func isKind(what interface{}, kinds ...reflect.Kind) bool {
|
|
target := what
|
|
if isJSONNumber(what) {
|
|
// JSON Numbers are strings!
|
|
target = *mustBeNumber(what)
|
|
}
|
|
targetKind := reflect.ValueOf(target).Kind()
|
|
for _, kind := range kinds {
|
|
if targetKind == kind {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// indexStringInSlice returns the index of the first instance of 'what' in s or -1 if it is not found in s.
|
|
func indexStringInSlice(s []string, what string) int {
|
|
for i := range s {
|
|
if s[i] == what {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func marshalToJSONString(value interface{}) (*string, error) {
|
|
|
|
mBytes, err := json.Marshal(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sBytes := string(mBytes)
|
|
return &sBytes, nil
|
|
}
|
|
|
|
func marshalWithoutNumber(value interface{}) (*string, error) {
|
|
|
|
// The JSON is decoded using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
|
|
// This means the numbers are internally still represented as strings and therefore 1.00 is unequal to 1
|
|
// One way to eliminate these differences is to decode and encode the JSON one more time without Decoder.UseNumber
|
|
// so that these differences in representation are removed
|
|
|
|
jsonString, err := marshalToJSONString(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var document interface{}
|
|
|
|
err = json.Unmarshal([]byte(*jsonString), &document)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return marshalToJSONString(document)
|
|
}
|
|
|
|
func isJSONNumber(what interface{}) bool {
|
|
|
|
switch what.(type) {
|
|
|
|
case json.Number:
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func checkJSONInteger(what interface{}) (isInt bool) {
|
|
|
|
jsonNumber := what.(json.Number)
|
|
|
|
bigFloat, isValidNumber := new(big.Rat).SetString(string(jsonNumber))
|
|
|
|
return isValidNumber && bigFloat.IsInt()
|
|
|
|
}
|
|
|
|
// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
|
|
const (
|
|
maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
|
|
minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
|
|
)
|
|
|
|
func mustBeInteger(what interface{}) *int {
|
|
|
|
if isJSONNumber(what) {
|
|
|
|
number := what.(json.Number)
|
|
|
|
isInt := checkJSONInteger(number)
|
|
|
|
if isInt {
|
|
|
|
int64Value, err := number.Int64()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
int32Value := int(int64Value)
|
|
return &int32Value
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func mustBeNumber(what interface{}) *big.Rat {
|
|
|
|
if isJSONNumber(what) {
|
|
number := what.(json.Number)
|
|
float64Value, success := new(big.Rat).SetString(string(number))
|
|
if success {
|
|
return float64Value
|
|
}
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
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
|
|
}
|