...

Source file src/github.com/xeipuuv/gojsonschema/jsonLoader.go

Documentation: github.com/xeipuuv/gojsonschema

     1  // Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
     2  //
     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  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // author           xeipuuv
    16  // author-github    https://github.com/xeipuuv
    17  // author-mail      xeipuuv@gmail.com
    18  //
    19  // repository-name  gojsonschema
    20  // repository-desc  An implementation of JSON Schema, based on IETF's draft v4 - Go language.
    21  //
    22  // description		Different strategies to load JSON files.
    23  // 					Includes References (file and HTTP), JSON strings and Go types.
    24  //
    25  // created          01-02-2015
    26  
    27  package gojsonschema
    28  
    29  import (
    30  	"bytes"
    31  	"encoding/json"
    32  	"errors"
    33  	"io"
    34  	"io/ioutil"
    35  	"net/http"
    36  	"net/url"
    37  	"os"
    38  	"path/filepath"
    39  	"runtime"
    40  	"strings"
    41  
    42  	"github.com/xeipuuv/gojsonreference"
    43  )
    44  
    45  var osFS = osFileSystem(os.Open)
    46  
    47  // JSONLoader defines the JSON loader interface
    48  type JSONLoader interface {
    49  	JsonSource() interface{}
    50  	LoadJSON() (interface{}, error)
    51  	JsonReference() (gojsonreference.JsonReference, error)
    52  	LoaderFactory() JSONLoaderFactory
    53  }
    54  
    55  // JSONLoaderFactory defines the JSON loader factory interface
    56  type JSONLoaderFactory interface {
    57  	// New creates a new JSON loader for the given source
    58  	New(source string) JSONLoader
    59  }
    60  
    61  // DefaultJSONLoaderFactory is the default JSON loader factory
    62  type DefaultJSONLoaderFactory struct {
    63  }
    64  
    65  // FileSystemJSONLoaderFactory is a JSON loader factory that uses http.FileSystem
    66  type FileSystemJSONLoaderFactory struct {
    67  	fs http.FileSystem
    68  }
    69  
    70  // New creates a new JSON loader for the given source
    71  func (d DefaultJSONLoaderFactory) New(source string) JSONLoader {
    72  	return &jsonReferenceLoader{
    73  		fs:     osFS,
    74  		source: source,
    75  	}
    76  }
    77  
    78  // New creates a new JSON loader for the given source
    79  func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader {
    80  	return &jsonReferenceLoader{
    81  		fs:     f.fs,
    82  		source: source,
    83  	}
    84  }
    85  
    86  // osFileSystem is a functional wrapper for os.Open that implements http.FileSystem.
    87  type osFileSystem func(string) (*os.File, error)
    88  
    89  // Opens a file with the given name
    90  func (o osFileSystem) Open(name string) (http.File, error) {
    91  	return o(name)
    92  }
    93  
    94  // JSON Reference loader
    95  // references are used to load JSONs from files and HTTP
    96  
    97  type jsonReferenceLoader struct {
    98  	fs     http.FileSystem
    99  	source string
   100  }
   101  
   102  func (l *jsonReferenceLoader) JsonSource() interface{} {
   103  	return l.source
   104  }
   105  
   106  func (l *jsonReferenceLoader) JsonReference() (gojsonreference.JsonReference, error) {
   107  	return gojsonreference.NewJsonReference(l.JsonSource().(string))
   108  }
   109  
   110  func (l *jsonReferenceLoader) LoaderFactory() JSONLoaderFactory {
   111  	return &FileSystemJSONLoaderFactory{
   112  		fs: l.fs,
   113  	}
   114  }
   115  
   116  // NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system.
   117  func NewReferenceLoader(source string) JSONLoader {
   118  	return &jsonReferenceLoader{
   119  		fs:     osFS,
   120  		source: source,
   121  	}
   122  }
   123  
   124  // NewReferenceLoaderFileSystem returns a JSON reference loader using the given source and file system.
   125  func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) JSONLoader {
   126  	return &jsonReferenceLoader{
   127  		fs:     fs,
   128  		source: source,
   129  	}
   130  }
   131  
   132  func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
   133  
   134  	var err error
   135  
   136  	reference, err := gojsonreference.NewJsonReference(l.JsonSource().(string))
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	refToURL := reference
   142  	refToURL.GetUrl().Fragment = ""
   143  
   144  	var document interface{}
   145  
   146  	if reference.HasFileScheme {
   147  
   148  		filename := strings.TrimPrefix(refToURL.String(), "file://")
   149  		filename, err = url.QueryUnescape(filename)
   150  
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  
   155  		if runtime.GOOS == "windows" {
   156  			// on Windows, a file URL may have an extra leading slash, use slashes
   157  			// instead of backslashes, and have spaces escaped
   158  			filename = strings.TrimPrefix(filename, "/")
   159  			filename = filepath.FromSlash(filename)
   160  		}
   161  
   162  		document, err = l.loadFromFile(filename)
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  
   167  	} else {
   168  
   169  		document, err = l.loadFromHTTP(refToURL.String())
   170  		if err != nil {
   171  			return nil, err
   172  		}
   173  
   174  	}
   175  
   176  	return document, nil
   177  
   178  }
   179  
   180  func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) {
   181  
   182  	// returned cached versions for metaschemas for drafts 4, 6 and 7
   183  	// for performance and allow for easier offline use
   184  	if metaSchema := drafts.GetMetaSchema(address); metaSchema != "" {
   185  		return decodeJSONUsingNumber(strings.NewReader(metaSchema))
   186  	}
   187  
   188  	resp, err := http.Get(address)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	// must return HTTP Status 200 OK
   194  	if resp.StatusCode != http.StatusOK {
   195  		return nil, errors.New(formatErrorDescription(Locale.HttpBadStatus(), ErrorDetails{"status": resp.Status}))
   196  	}
   197  
   198  	bodyBuff, err := ioutil.ReadAll(resp.Body)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	return decodeJSONUsingNumber(bytes.NewReader(bodyBuff))
   204  }
   205  
   206  func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
   207  	f, err := l.fs.Open(path)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	defer f.Close()
   212  
   213  	bodyBuff, err := ioutil.ReadAll(f)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  
   218  	return decodeJSONUsingNumber(bytes.NewReader(bodyBuff))
   219  
   220  }
   221  
   222  // JSON string loader
   223  
   224  type jsonStringLoader struct {
   225  	source string
   226  }
   227  
   228  func (l *jsonStringLoader) JsonSource() interface{} {
   229  	return l.source
   230  }
   231  
   232  func (l *jsonStringLoader) JsonReference() (gojsonreference.JsonReference, error) {
   233  	return gojsonreference.NewJsonReference("#")
   234  }
   235  
   236  func (l *jsonStringLoader) LoaderFactory() JSONLoaderFactory {
   237  	return &DefaultJSONLoaderFactory{}
   238  }
   239  
   240  // NewStringLoader creates a new JSONLoader, taking a string as source
   241  func NewStringLoader(source string) JSONLoader {
   242  	return &jsonStringLoader{source: source}
   243  }
   244  
   245  func (l *jsonStringLoader) LoadJSON() (interface{}, error) {
   246  
   247  	return decodeJSONUsingNumber(strings.NewReader(l.JsonSource().(string)))
   248  
   249  }
   250  
   251  // JSON bytes loader
   252  
   253  type jsonBytesLoader struct {
   254  	source []byte
   255  }
   256  
   257  func (l *jsonBytesLoader) JsonSource() interface{} {
   258  	return l.source
   259  }
   260  
   261  func (l *jsonBytesLoader) JsonReference() (gojsonreference.JsonReference, error) {
   262  	return gojsonreference.NewJsonReference("#")
   263  }
   264  
   265  func (l *jsonBytesLoader) LoaderFactory() JSONLoaderFactory {
   266  	return &DefaultJSONLoaderFactory{}
   267  }
   268  
   269  // NewBytesLoader creates a new JSONLoader, taking a `[]byte` as source
   270  func NewBytesLoader(source []byte) JSONLoader {
   271  	return &jsonBytesLoader{source: source}
   272  }
   273  
   274  func (l *jsonBytesLoader) LoadJSON() (interface{}, error) {
   275  	return decodeJSONUsingNumber(bytes.NewReader(l.JsonSource().([]byte)))
   276  }
   277  
   278  // JSON Go (types) loader
   279  // used to load JSONs from the code as maps, interface{}, structs ...
   280  
   281  type jsonGoLoader struct {
   282  	source interface{}
   283  }
   284  
   285  func (l *jsonGoLoader) JsonSource() interface{} {
   286  	return l.source
   287  }
   288  
   289  func (l *jsonGoLoader) JsonReference() (gojsonreference.JsonReference, error) {
   290  	return gojsonreference.NewJsonReference("#")
   291  }
   292  
   293  func (l *jsonGoLoader) LoaderFactory() JSONLoaderFactory {
   294  	return &DefaultJSONLoaderFactory{}
   295  }
   296  
   297  // NewGoLoader creates a new JSONLoader from a given Go struct
   298  func NewGoLoader(source interface{}) JSONLoader {
   299  	return &jsonGoLoader{source: source}
   300  }
   301  
   302  func (l *jsonGoLoader) LoadJSON() (interface{}, error) {
   303  
   304  	// convert it to a compliant JSON first to avoid types "mismatches"
   305  
   306  	jsonBytes, err := json.Marshal(l.JsonSource())
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	return decodeJSONUsingNumber(bytes.NewReader(jsonBytes))
   312  
   313  }
   314  
   315  type jsonIOLoader struct {
   316  	buf *bytes.Buffer
   317  }
   318  
   319  // NewReaderLoader creates a new JSON loader using the provided io.Reader
   320  func NewReaderLoader(source io.Reader) (JSONLoader, io.Reader) {
   321  	buf := &bytes.Buffer{}
   322  	return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf)
   323  }
   324  
   325  // NewWriterLoader creates a new JSON loader using the provided io.Writer
   326  func NewWriterLoader(source io.Writer) (JSONLoader, io.Writer) {
   327  	buf := &bytes.Buffer{}
   328  	return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf)
   329  }
   330  
   331  func (l *jsonIOLoader) JsonSource() interface{} {
   332  	return l.buf.String()
   333  }
   334  
   335  func (l *jsonIOLoader) LoadJSON() (interface{}, error) {
   336  	return decodeJSONUsingNumber(l.buf)
   337  }
   338  
   339  func (l *jsonIOLoader) JsonReference() (gojsonreference.JsonReference, error) {
   340  	return gojsonreference.NewJsonReference("#")
   341  }
   342  
   343  func (l *jsonIOLoader) LoaderFactory() JSONLoaderFactory {
   344  	return &DefaultJSONLoaderFactory{}
   345  }
   346  
   347  // JSON raw loader
   348  // In case the JSON is already marshalled to interface{} use this loader
   349  // This is used for testing as otherwise there is no guarantee the JSON is marshalled
   350  // "properly" by using https://golang.org/pkg/encoding/json/#Decoder.UseNumber
   351  type jsonRawLoader struct {
   352  	source interface{}
   353  }
   354  
   355  // NewRawLoader creates a new JSON raw loader for the given source
   356  func NewRawLoader(source interface{}) JSONLoader {
   357  	return &jsonRawLoader{source: source}
   358  }
   359  func (l *jsonRawLoader) JsonSource() interface{} {
   360  	return l.source
   361  }
   362  func (l *jsonRawLoader) LoadJSON() (interface{}, error) {
   363  	return l.source, nil
   364  }
   365  func (l *jsonRawLoader) JsonReference() (gojsonreference.JsonReference, error) {
   366  	return gojsonreference.NewJsonReference("#")
   367  }
   368  func (l *jsonRawLoader) LoaderFactory() JSONLoaderFactory {
   369  	return &DefaultJSONLoaderFactory{}
   370  }
   371  
   372  func decodeJSONUsingNumber(r io.Reader) (interface{}, error) {
   373  
   374  	var document interface{}
   375  
   376  	decoder := json.NewDecoder(r)
   377  	decoder.UseNumber()
   378  
   379  	err := decoder.Decode(&document)
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  
   384  	return document, nil
   385  
   386  }
   387  

View as plain text