jsonLoader.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. // author xeipuuv
  15. // author-github https://github.com/xeipuuv
  16. // author-mail [email protected]
  17. //
  18. // repository-name gojsonschema
  19. // repository-desc An implementation of JSON Schema, based on IETF's draft v4 - Go language.
  20. //
  21. // description Different strategies to load JSON files.
  22. // Includes References (file and HTTP), JSON strings and Go types.
  23. //
  24. // created 01-02-2015
  25. package gojsonschema
  26. import (
  27. "bytes"
  28. "encoding/json"
  29. "errors"
  30. "io"
  31. "io/ioutil"
  32. "net/http"
  33. "path/filepath"
  34. "runtime"
  35. "strings"
  36. "github.com/xeipuuv/gojsonreference"
  37. )
  38. // JSON loader interface
  39. type JSONLoader interface {
  40. jsonSource() interface{}
  41. loadJSON() (interface{}, error)
  42. loadSchema() (*Schema, error)
  43. }
  44. // JSON Reference loader
  45. // references are used to load JSONs from files and HTTP
  46. type jsonReferenceLoader struct {
  47. source string
  48. }
  49. func (l *jsonReferenceLoader) jsonSource() interface{} {
  50. return l.source
  51. }
  52. func NewReferenceLoader(source string) *jsonReferenceLoader {
  53. return &jsonReferenceLoader{source: source}
  54. }
  55. func (l *jsonReferenceLoader) loadJSON() (interface{}, error) {
  56. var err error
  57. reference, err := gojsonreference.NewJsonReference(l.jsonSource().(string))
  58. if err != nil {
  59. return nil, err
  60. }
  61. refToUrl := reference
  62. refToUrl.GetUrl().Fragment = ""
  63. var document interface{}
  64. if reference.HasFileScheme {
  65. filename := strings.Replace(refToUrl.String(), "file://", "", -1)
  66. if runtime.GOOS == "windows" {
  67. // on Windows, a file URL may have an extra leading slash, use slashes
  68. // instead of backslashes, and have spaces escaped
  69. if strings.HasPrefix(filename, "/") {
  70. filename = filename[1:]
  71. }
  72. filename = filepath.FromSlash(filename)
  73. filename = strings.Replace(filename, "%20", " ", -1)
  74. }
  75. document, err = l.loadFromFile(filename)
  76. if err != nil {
  77. return nil, err
  78. }
  79. } else {
  80. document, err = l.loadFromHTTP(refToUrl.String())
  81. if err != nil {
  82. return nil, err
  83. }
  84. }
  85. return document, nil
  86. }
  87. func (l *jsonReferenceLoader) loadSchema() (*Schema, error) {
  88. var err error
  89. d := Schema{}
  90. d.pool = newSchemaPool()
  91. d.referencePool = newSchemaReferencePool()
  92. d.documentReference, err = gojsonreference.NewJsonReference(l.jsonSource().(string))
  93. if err != nil {
  94. return nil, err
  95. }
  96. spd, err := d.pool.GetDocument(d.documentReference)
  97. if err != nil {
  98. return nil, err
  99. }
  100. err = d.parse(spd.Document)
  101. if err != nil {
  102. return nil, err
  103. }
  104. return &d, nil
  105. }
  106. func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) {
  107. resp, err := http.Get(address)
  108. if err != nil {
  109. return nil, err
  110. }
  111. // must return HTTP Status 200 OK
  112. if resp.StatusCode != http.StatusOK {
  113. return nil, errors.New(formatErrorDescription(Locale.httpBadStatus(), ErrorDetails{"status": resp.Status}))
  114. }
  115. bodyBuff, err := ioutil.ReadAll(resp.Body)
  116. if err != nil {
  117. return nil, err
  118. }
  119. return decodeJsonUsingNumber(bytes.NewReader(bodyBuff))
  120. }
  121. func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
  122. bodyBuff, err := ioutil.ReadFile(path)
  123. if err != nil {
  124. return nil, err
  125. }
  126. return decodeJsonUsingNumber(bytes.NewReader(bodyBuff))
  127. }
  128. // JSON string loader
  129. type jsonStringLoader struct {
  130. source string
  131. }
  132. func (l *jsonStringLoader) jsonSource() interface{} {
  133. return l.source
  134. }
  135. func NewStringLoader(source string) *jsonStringLoader {
  136. return &jsonStringLoader{source: source}
  137. }
  138. func (l *jsonStringLoader) loadJSON() (interface{}, error) {
  139. return decodeJsonUsingNumber(strings.NewReader(l.jsonSource().(string)))
  140. }
  141. func (l *jsonStringLoader) loadSchema() (*Schema, error) {
  142. var err error
  143. document, err := l.loadJSON()
  144. if err != nil {
  145. return nil, err
  146. }
  147. d := Schema{}
  148. d.pool = newSchemaPool()
  149. d.referencePool = newSchemaReferencePool()
  150. d.documentReference, err = gojsonreference.NewJsonReference("#")
  151. d.pool.SetStandaloneDocument(document)
  152. if err != nil {
  153. return nil, err
  154. }
  155. err = d.parse(document)
  156. if err != nil {
  157. return nil, err
  158. }
  159. return &d, nil
  160. }
  161. // JSON Go (types) loader
  162. // used to load JSONs from the code as maps, interface{}, structs ...
  163. type jsonGoLoader struct {
  164. source interface{}
  165. }
  166. func (l *jsonGoLoader) jsonSource() interface{} {
  167. return l.source
  168. }
  169. func NewGoLoader(source interface{}) *jsonGoLoader {
  170. return &jsonGoLoader{source: source}
  171. }
  172. func (l *jsonGoLoader) loadJSON() (interface{}, error) {
  173. // convert it to a compliant JSON first to avoid types "mismatches"
  174. jsonBytes, err := json.Marshal(l.jsonSource())
  175. if err != nil {
  176. return nil, err
  177. }
  178. return decodeJsonUsingNumber(bytes.NewReader(jsonBytes))
  179. }
  180. func (l *jsonGoLoader) loadSchema() (*Schema, error) {
  181. var err error
  182. document, err := l.loadJSON()
  183. if err != nil {
  184. return nil, err
  185. }
  186. d := Schema{}
  187. d.pool = newSchemaPool()
  188. d.referencePool = newSchemaReferencePool()
  189. d.documentReference, err = gojsonreference.NewJsonReference("#")
  190. d.pool.SetStandaloneDocument(document)
  191. if err != nil {
  192. return nil, err
  193. }
  194. err = d.parse(document)
  195. if err != nil {
  196. return nil, err
  197. }
  198. return &d, nil
  199. }
  200. func decodeJsonUsingNumber(r io.Reader) (interface{}, error) {
  201. var document interface{}
  202. decoder := json.NewDecoder(r)
  203. decoder.UseNumber()
  204. err := decoder.Decode(&document)
  205. if err != nil {
  206. return nil, err
  207. }
  208. return document, nil
  209. }