├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── jason.go └── jason_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.1 5 | - 1.2 6 | - 1.3 7 | - tip 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Anton Holmquist, anton.holmquist@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jason is an easy-to-use JSON library for Go. 2 | 3 | [![Build Status](https://img.shields.io/travis/antonholmquist/jason.svg?style=flat)](https://travis-ci.org/antonholmquist/jason) [![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/antonholmquist/jason) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/antonholmquist/jason/master/LICENSE) 4 | 5 | # About 6 | 7 | Jason is designed to be convenient for reading arbitrary JSON while still honoring the strictness of the language. Inspired by other libraries and improved to work well for common use cases. It currently focuses on reading JSON data rather than creating it. [API Documentation](http://godoc.org/github.com/antonholmquist/jason) can be found on godoc.org. 8 | 9 | ## Install 10 | 11 | ```shell 12 | go get github.com/antonholmquist/jason 13 | ``` 14 | 15 | ## Import 16 | 17 | ```go 18 | import ( 19 | "github.com/antonholmquist/jason" 20 | ) 21 | ``` 22 | 23 | ## Data types 24 | 25 | The following golang values are used to represent JSON data types. It is consistent with how `encoding/json` uses primitive types. 26 | 27 | - `bool`, for JSON booleans 28 | - `json.Number/float64/int64`, for JSON numbers 29 | - `string`, for JSON strings 30 | - `[]*Value`, for JSON arrays 31 | - `map[string]*Value`, for JSON objects 32 | - `nil` for JSON null 33 | 34 | ## Examples 35 | 36 | ### Create from bytes 37 | 38 | Create object from bytes. Returns an error if the bytes are not valid JSON. 39 | 40 | ```go 41 | v, err := jason.NewObjectFromBytes(b) 42 | 43 | ``` 44 | 45 | If the root object is unknown or not an object, use `NewValueFromBytes` instead. It can then be typecasted using one of the conversion methods provided by the library, for instance `Array()` or `String()`. 46 | 47 | ```go 48 | v, err := jason.NewValueFromBytes(b) 49 | 50 | ``` 51 | 52 | ### Create from a reader (like a http response) 53 | 54 | Create value from a io.reader. Returns an error if the string couldn't be parsed. 55 | 56 | ```go 57 | v, err := jason.NewObjectFromReader(res.Body) 58 | 59 | ``` 60 | 61 | ### Read values 62 | 63 | Reading values is easy. If the key path is invalid or type doesn't match, it will return an error and the default value. 64 | 65 | ```go 66 | name, err := v.GetString("name") 67 | age, err := v.GetInt64("age") 68 | verified, err := v.GetBoolean("verified") 69 | education, err := v.GetObject("education") 70 | friends, err := v.GetObjectArray("friends") 71 | interests, err := v.GetStringArray("interests") 72 | 73 | ``` 74 | 75 | ### Read nested values 76 | 77 | Reading nested values is easy. If the path is invalid or type doesn't match, it will return the default value and an error. 78 | 79 | ```go 80 | name, err := v.GetString("person", "name") 81 | age, err := v.GetInt64("person", "age") 82 | verified, err := v.GetBoolean("person", "verified") 83 | education, err := v.GetObject("person", "education") 84 | friends, err := v.GetObjectArray("person", "friends") 85 | 86 | ``` 87 | 88 | ### Loop through array 89 | 90 | Looping through an array is done with `GetValueArray()` or `GetObjectArray()`. It returns an error if the value at that keypath is null (or something else than an array). 91 | 92 | ```go 93 | friends, err := person.GetObjectArray("friends") 94 | for _, friend := range friends { 95 | name, err := friend.GetString("name") 96 | age, err := friend.GetNumber("age") 97 | } 98 | ``` 99 | 100 | ### Loop through object 101 | 102 | Looping through an object is easy. `GetObject()` returns an error if the value at that keypath is null (or something else than an object). 103 | 104 | ```go 105 | person, err := person.GetObject("person") 106 | for key, value := range person.Map() { 107 | ... 108 | } 109 | ``` 110 | 111 | ## Sample App 112 | 113 | Example project: 114 | 115 | ```go 116 | package main 117 | 118 | import ( 119 | "github.com/antonholmquist/jason" 120 | "log" 121 | ) 122 | 123 | func main() { 124 | 125 | exampleJSON := `{ 126 | "name": "Walter White", 127 | "age": 51, 128 | "children": [ 129 | "junior", 130 | "holly" 131 | ], 132 | "other": { 133 | "occupation": "chemist", 134 | "years": 23 135 | } 136 | }` 137 | 138 | v, _ := jason.NewObjectFromBytes([]byte(exampleJSON)) 139 | 140 | name, _ := v.GetString("name") 141 | age, _ := v.GetNumber("age") 142 | occupation, _ := v.GetString("other", "occupation") 143 | years, _ := v.GetNumber("other", "years") 144 | 145 | log.Println("age:", age) 146 | log.Println("name:", name) 147 | log.Println("occupation:", occupation) 148 | log.Println("years:", years) 149 | 150 | children, _ := v.GetStringArray("children") 151 | for i, child := range children { 152 | log.Printf("child %d: %s", i, child) 153 | } 154 | 155 | others, _ := v.GetObject("other") 156 | 157 | for _, value := range others.Map() { 158 | 159 | s, sErr := value.String() 160 | n, nErr := value.Number() 161 | 162 | if sErr == nil { 163 | log.Println("string value: ", s) 164 | } else if nErr == nil { 165 | log.Println("number value: ", n) 166 | } 167 | } 168 | } 169 | 170 | ``` 171 | 172 | ## Documentation 173 | 174 | Documentation can be found on godoc: 175 | 176 | https://godoc.org/github.com/antonholmquist/jason 177 | 178 | ## Test 179 | To run the project tests: 180 | 181 | ```shell 182 | go test 183 | ``` 184 | 185 | ## Compatibility 186 | 187 | Go 1.1 and up. 188 | 189 | ## Where does the name come from? 190 | 191 | I remembered it from an email one of our projects managers sent a couple of years ago. 192 | 193 | > "Don't worry. We can handle both XML and Jason" 194 | 195 | ## Author 196 | 197 | Anton Holmquist, http://twitter.com/antonholmquist 198 | -------------------------------------------------------------------------------- /jason.go: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a 2 | // license that can be found in the LICENSE file. 3 | 4 | // Jason is designed to be convenient for reading arbitrary JSON while still honoring the strictness of the language. 5 | // Inspired by other libraries and improved to work well for common use cases. 6 | // It currently focuses on reading JSON data rather than creating it. 7 | // 8 | // Examples 9 | // 10 | // JSON is a commonly used data transfer format, so usually the data you want to read comes either as bytes or as an io.Reader. 11 | // 12 | // Create an object from bytes: 13 | // v, err := jason.NewObjectFromBytes(b) 14 | // .. or from a net/http response body: 15 | // v, err := jason.NewObjectFromReader(res.body) 16 | // 17 | // Read values 18 | // 19 | // Reading values is done with Get(keys ...) or the generic Get(keys ...). 20 | // If the key path is invalid or the type doesn't match, it will return an error and the default value. 21 | // 22 | // name, err := v.GetString("name") 23 | // age, err := v.GetNumber("age") 24 | // verified, err := v.GetBoolean("verified") 25 | // education, err := v.GetObject("education") 26 | // friends, err := v.GetObjectArray("friends") 27 | // 28 | // Loop through array 29 | // 30 | // Getting an array is done by GetArray() or the generic GetValueArray(). It returns an error if the value at that keypath is null (or something else than the type). 31 | // 32 | // friends, err := person.GetObjectArray("friends") 33 | // for _, friend := range friends { 34 | // name, err := friend.GetString("name") 35 | // age, err := friend.GetNumber("age") 36 | // } 37 | // 38 | // Loop through keys of object 39 | // 40 | // Looping through an object is done by first getting it with `GetObject()` and then range on the Map(). 41 | // The GetObject() method returns an error if the value at that keypath is null (or something else than an object). 42 | // 43 | // person, err := person.GetObject("person") 44 | // for key, value := range person.Map() { 45 | // ... 46 | // } 47 | package jason 48 | 49 | import ( 50 | "bytes" 51 | "encoding/json" 52 | "errors" 53 | "fmt" 54 | "io" 55 | ) 56 | 57 | // Error values returned when validation functions fail 58 | var ( 59 | ErrNotNull = errors.New("is not null") 60 | ErrNotArray = errors.New("Not an array") 61 | ErrNotNumber = errors.New("not a number") 62 | ErrNotBool = errors.New("no bool") 63 | ErrNotObject = errors.New("not an object") 64 | ErrNotObjectArray = errors.New("not an object array") 65 | ErrNotString = errors.New("not a string") 66 | ) 67 | 68 | type KeyNotFoundError struct { 69 | Key string 70 | } 71 | 72 | func (k KeyNotFoundError) Error() string { 73 | if k.Key != "" { 74 | return fmt.Sprintf("key '%s' not found", k.Key) 75 | } 76 | 77 | return "key not found" 78 | } 79 | 80 | // Value represents an arbitrary JSON value. 81 | // It may contain a bool, number, string, object, array or null. 82 | type Value struct { 83 | data interface{} 84 | exists bool // Used to separate nil and non-existing values 85 | } 86 | 87 | // Object represents an object JSON object. 88 | // It inherets from Value but with an additional method to access 89 | // a map representation of it's content. It's useful when iterating. 90 | type Object struct { 91 | Value 92 | m map[string]*Value 93 | valid bool 94 | } 95 | 96 | // Marshal into bytes. 97 | func (v *Object) MarshalJSON() ([]byte, error) { 98 | return json.Marshal(v.m) 99 | } 100 | 101 | // Returns the golang map. 102 | // Needed when iterating through the values of the object. 103 | func (v *Object) Map() map[string]*Value { 104 | return v.m 105 | } 106 | 107 | // Creates a new value from an io.reader. 108 | // Returns an error if the reader does not contain valid json. 109 | // Useful for parsing the body of a net/http response. 110 | // Example: NewFromReader(res.Body) 111 | func NewValueFromReader(reader io.Reader) (*Value, error) { 112 | j := new(Value) 113 | d := json.NewDecoder(reader) 114 | d.UseNumber() 115 | err := d.Decode(&j.data) 116 | return j, err 117 | } 118 | 119 | // Creates a new value from bytes. 120 | // Returns an error if the bytes are not valid json. 121 | func NewValueFromBytes(b []byte) (*Value, error) { 122 | r := bytes.NewReader(b) 123 | return NewValueFromReader(r) 124 | } 125 | 126 | func objectFromValue(v *Value, err error) (*Object, error) { 127 | if err != nil { 128 | return nil, err 129 | } 130 | 131 | o, err := v.Object() 132 | 133 | if err != nil { 134 | return nil, err 135 | } 136 | 137 | return o, nil 138 | } 139 | 140 | func NewObjectFromBytes(b []byte) (*Object, error) { 141 | return objectFromValue(NewValueFromBytes(b)) 142 | } 143 | 144 | func NewObjectFromReader(reader io.Reader) (*Object, error) { 145 | return objectFromValue(NewValueFromReader(reader)) 146 | } 147 | 148 | // Marshal into bytes. 149 | func (v *Value) Marshal() ([]byte, error) { 150 | return json.Marshal(v.data) 151 | } 152 | 153 | // Marshal into bytes 154 | func (v *Value) MarshalJSON() ([]byte, error) { 155 | return v.Marshal() 156 | } 157 | 158 | // Get the interyling data as interface 159 | func (v *Value) Interface() interface{} { 160 | return v.data 161 | } 162 | 163 | // Private Get 164 | func (v *Value) get(key string) (*Value, error) { 165 | 166 | // Assume this is an object 167 | obj, err := v.Object() 168 | 169 | if err == nil { 170 | child, ok := obj.Map()[key] 171 | if ok { 172 | return child, nil 173 | } else { 174 | return nil, KeyNotFoundError{key} 175 | } 176 | } 177 | 178 | return nil, err 179 | } 180 | 181 | // Private get path 182 | func (v *Value) getPath(keys []string) (*Value, error) { 183 | current := v 184 | var err error 185 | for _, key := range keys { 186 | current, err = current.get(key) 187 | 188 | if err != nil { 189 | return nil, err 190 | } 191 | } 192 | return current, nil 193 | } 194 | 195 | // Gets the value at key path. 196 | // Returns error if the value does not exist. 197 | // Consider using the more specific Get(..) methods instead. 198 | // Example: 199 | // value, err := GetValue("address", "street") 200 | func (v *Object) GetValue(keys ...string) (*Value, error) { 201 | return v.getPath(keys) 202 | } 203 | 204 | // Gets the value at key path and attempts to typecast the value into an object. 205 | // Returns error if the value is not a json object. 206 | // Example: 207 | // object, err := GetObject("person", "address") 208 | func (v *Object) GetObject(keys ...string) (*Object, error) { 209 | child, err := v.getPath(keys) 210 | 211 | if err != nil { 212 | return nil, err 213 | } else { 214 | 215 | obj, err := child.Object() 216 | 217 | if err != nil { 218 | return nil, err 219 | } else { 220 | return obj, nil 221 | } 222 | 223 | } 224 | } 225 | 226 | // Gets the value at key path and attempts to typecast the value into a string. 227 | // Returns error if the value is not a json string. 228 | // Example: 229 | // string, err := GetString("address", "street") 230 | func (v *Object) GetString(keys ...string) (string, error) { 231 | child, err := v.getPath(keys) 232 | 233 | if err != nil { 234 | return "", err 235 | } else { 236 | return child.String() 237 | } 238 | } 239 | 240 | // Gets the value at key path and attempts to typecast the value into null. 241 | // Returns error if the value is not json null. 242 | // Example: 243 | // err := GetNull("address", "street") 244 | func (v *Object) GetNull(keys ...string) error { 245 | child, err := v.getPath(keys) 246 | 247 | if err != nil { 248 | return err 249 | } 250 | 251 | return child.Null() 252 | } 253 | 254 | // Gets the value at key path and attempts to typecast the value into a number. 255 | // Returns error if the value is not a json number. 256 | // Example: 257 | // n, err := GetNumber("address", "street_number") 258 | func (v *Object) GetNumber(keys ...string) (json.Number, error) { 259 | child, err := v.getPath(keys) 260 | 261 | if err != nil { 262 | return "", err 263 | } else { 264 | 265 | n, err := child.Number() 266 | 267 | if err != nil { 268 | return "", err 269 | } else { 270 | return n, nil 271 | } 272 | } 273 | } 274 | 275 | // Gets the value at key path and attempts to typecast the value into a float64. 276 | // Returns error if the value is not a json number. 277 | // Example: 278 | // n, err := GetNumber("address", "street_number") 279 | func (v *Object) GetFloat64(keys ...string) (float64, error) { 280 | child, err := v.getPath(keys) 281 | 282 | if err != nil { 283 | return 0, err 284 | } else { 285 | 286 | n, err := child.Float64() 287 | 288 | if err != nil { 289 | return 0, err 290 | } else { 291 | return n, nil 292 | } 293 | } 294 | } 295 | 296 | // Gets the value at key path and attempts to typecast the value into a float64. 297 | // Returns error if the value is not a json number. 298 | // Example: 299 | // n, err := GetNumber("address", "street_number") 300 | func (v *Object) GetInt64(keys ...string) (int64, error) { 301 | child, err := v.getPath(keys) 302 | 303 | if err != nil { 304 | return 0, err 305 | } else { 306 | 307 | n, err := child.Int64() 308 | 309 | if err != nil { 310 | return 0, err 311 | } else { 312 | return n, nil 313 | } 314 | } 315 | } 316 | 317 | // Gets the value at key path and attempts to typecast the value into a float64. 318 | // Returns error if the value is not a json number. 319 | // Example: 320 | // v, err := GetInterface("address", "anything") 321 | func (v *Object) GetInterface(keys ...string) (interface{}, error) { 322 | child, err := v.getPath(keys) 323 | 324 | if err != nil { 325 | return nil, err 326 | } else { 327 | return child.Interface(), nil 328 | } 329 | } 330 | 331 | // Gets the value at key path and attempts to typecast the value into a bool. 332 | // Returns error if the value is not a json boolean. 333 | // Example: 334 | // married, err := GetBoolean("person", "married") 335 | func (v *Object) GetBoolean(keys ...string) (bool, error) { 336 | child, err := v.getPath(keys) 337 | 338 | if err != nil { 339 | return false, err 340 | } 341 | 342 | return child.Boolean() 343 | } 344 | 345 | // Gets the value at key path and attempts to typecast the value into an array. 346 | // Returns error if the value is not a json array. 347 | // Consider using the more specific GetArray() since it may reduce later type casts. 348 | // Example: 349 | // friends, err := GetValueArray("person", "friends") 350 | // for i, friend := range friends { 351 | // ... // friend will be of type Value here 352 | // } 353 | func (v *Object) GetValueArray(keys ...string) ([]*Value, error) { 354 | child, err := v.getPath(keys) 355 | 356 | if err != nil { 357 | return nil, err 358 | } else { 359 | 360 | return child.Array() 361 | 362 | } 363 | } 364 | 365 | // Gets the value at key path and attempts to typecast the value into an array of objects. 366 | // Returns error if the value is not a json array or if any of the contained objects are not objects. 367 | // Example: 368 | // friends, err := GetObjectArray("person", "friends") 369 | // for i, friend := range friends { 370 | // ... // friend will be of type Object here 371 | // } 372 | func (v *Object) GetObjectArray(keys ...string) ([]*Object, error) { 373 | child, err := v.getPath(keys) 374 | 375 | if err != nil { 376 | return nil, err 377 | } else { 378 | 379 | array, err := child.Array() 380 | 381 | if err != nil { 382 | return nil, err 383 | } else { 384 | 385 | typedArray := make([]*Object, len(array)) 386 | 387 | for index, arrayItem := range array { 388 | typedArrayItem, err := arrayItem. 389 | Object() 390 | 391 | if err != nil { 392 | return nil, err 393 | } else { 394 | typedArray[index] = typedArrayItem 395 | } 396 | 397 | } 398 | return typedArray, nil 399 | } 400 | } 401 | } 402 | 403 | // Gets the value at key path and attempts to typecast the value into an array of string. 404 | // Returns error if the value is not a json array or if any of the contained objects are not strings. 405 | // Gets the value at key path and attempts to typecast the value into an array of objects. 406 | // Returns error if the value is not a json array or if any of the contained objects are not objects. 407 | // Example: 408 | // friendNames, err := GetStringArray("person", "friend_names") 409 | // for i, friendName := range friendNames { 410 | // ... // friendName will be of type string here 411 | // } 412 | func (v *Object) GetStringArray(keys ...string) ([]string, error) { 413 | child, err := v.getPath(keys) 414 | 415 | if err != nil { 416 | return nil, err 417 | } else { 418 | 419 | array, err := child.Array() 420 | 421 | if err != nil { 422 | return nil, err 423 | } else { 424 | 425 | typedArray := make([]string, len(array)) 426 | 427 | for index, arrayItem := range array { 428 | typedArrayItem, err := arrayItem.String() 429 | 430 | if err != nil { 431 | return nil, err 432 | } else { 433 | typedArray[index] = typedArrayItem 434 | } 435 | 436 | } 437 | return typedArray, nil 438 | } 439 | } 440 | } 441 | 442 | // Gets the value at key path and attempts to typecast the value into an array of numbers. 443 | // Returns error if the value is not a json array or if any of the contained objects are not numbers. 444 | // Example: 445 | // friendAges, err := GetNumberArray("person", "friend_ages") 446 | // for i, friendAge := range friendAges { 447 | // ... // friendAge will be of type float64 here 448 | // } 449 | func (v *Object) GetNumberArray(keys ...string) ([]json.Number, error) { 450 | child, err := v.getPath(keys) 451 | 452 | if err != nil { 453 | return nil, err 454 | } else { 455 | 456 | array, err := child.Array() 457 | 458 | if err != nil { 459 | return nil, err 460 | } else { 461 | 462 | typedArray := make([]json.Number, len(array)) 463 | 464 | for index, arrayItem := range array { 465 | typedArrayItem, err := arrayItem.Number() 466 | 467 | if err != nil { 468 | return nil, err 469 | } else { 470 | typedArray[index] = typedArrayItem 471 | } 472 | 473 | } 474 | return typedArray, nil 475 | } 476 | } 477 | } 478 | 479 | // Gets the value at key path and attempts to typecast the value into an array of floats. 480 | // Returns error if the value is not a json array or if any of the contained objects are not numbers. 481 | func (v *Object) GetFloat64Array(keys ...string) ([]float64, error) { 482 | child, err := v.getPath(keys) 483 | 484 | if err != nil { 485 | return nil, err 486 | } else { 487 | 488 | array, err := child.Array() 489 | 490 | if err != nil { 491 | return nil, err 492 | } else { 493 | 494 | typedArray := make([]float64, len(array)) 495 | 496 | for index, arrayItem := range array { 497 | typedArrayItem, err := arrayItem.Float64() 498 | 499 | if err != nil { 500 | return nil, err 501 | } else { 502 | typedArray[index] = typedArrayItem 503 | } 504 | 505 | } 506 | return typedArray, nil 507 | } 508 | } 509 | } 510 | 511 | // Gets the value at key path and attempts to typecast the value into an array of ints. 512 | // Returns error if the value is not a json array or if any of the contained objects are not numbers. 513 | func (v *Object) GetInt64Array(keys ...string) ([]int64, error) { 514 | child, err := v.getPath(keys) 515 | 516 | if err != nil { 517 | return nil, err 518 | } else { 519 | 520 | array, err := child.Array() 521 | 522 | if err != nil { 523 | return nil, err 524 | } else { 525 | 526 | typedArray := make([]int64, len(array)) 527 | 528 | for index, arrayItem := range array { 529 | typedArrayItem, err := arrayItem.Int64() 530 | 531 | if err != nil { 532 | return nil, err 533 | } else { 534 | typedArray[index] = typedArrayItem 535 | } 536 | 537 | } 538 | return typedArray, nil 539 | } 540 | } 541 | } 542 | 543 | // Gets the value at key path and attempts to typecast the value into an array of bools. 544 | // Returns error if the value is not a json array or if any of the contained objects are not booleans. 545 | func (v *Object) GetBooleanArray(keys ...string) ([]bool, error) { 546 | child, err := v.getPath(keys) 547 | 548 | if err != nil { 549 | return nil, err 550 | } else { 551 | 552 | array, err := child.Array() 553 | 554 | if err != nil { 555 | return nil, err 556 | } else { 557 | 558 | typedArray := make([]bool, len(array)) 559 | 560 | for index, arrayItem := range array { 561 | typedArrayItem, err := arrayItem.Boolean() 562 | 563 | if err != nil { 564 | return nil, err 565 | } else { 566 | typedArray[index] = typedArrayItem 567 | } 568 | 569 | } 570 | return typedArray, nil 571 | } 572 | } 573 | } 574 | 575 | // Gets the value at key path and attempts to typecast the value into an array of nulls. 576 | // Returns length, or an error if the value is not a json array or if any of the contained objects are not nulls. 577 | func (v *Object) GetNullArray(keys ...string) (int64, error) { 578 | child, err := v.getPath(keys) 579 | 580 | if err != nil { 581 | return 0, err 582 | } else { 583 | 584 | array, err := child.Array() 585 | 586 | if err != nil { 587 | return 0, err 588 | } else { 589 | 590 | var length int64 = 0 591 | 592 | for _, arrayItem := range array { 593 | err := arrayItem.Null() 594 | 595 | if err != nil { 596 | return 0, err 597 | } else { 598 | length++ 599 | } 600 | 601 | } 602 | return length, nil 603 | } 604 | } 605 | } 606 | 607 | // Returns an error if the value is not actually null 608 | func (v *Value) Null() error { 609 | var valid bool 610 | 611 | // Check the type of this data 612 | switch v.data.(type) { 613 | case nil: 614 | valid = v.exists // Valid only if j also exists, since other values could possibly also be nil 615 | break 616 | } 617 | 618 | if valid { 619 | return nil 620 | } 621 | 622 | return ErrNotNull 623 | 624 | } 625 | 626 | // Attempts to typecast the current value into an array. 627 | // Returns error if the current value is not a json array. 628 | // Example: 629 | // friendsArray, err := friendsValue.Array() 630 | func (v *Value) Array() ([]*Value, error) { 631 | var valid bool 632 | 633 | // Check the type of this data 634 | switch v.data.(type) { 635 | case []interface{}: 636 | valid = true 637 | break 638 | } 639 | 640 | // Unsure if this is a good way to use slices, it's probably not 641 | var slice []*Value 642 | 643 | if valid { 644 | 645 | for _, element := range v.data.([]interface{}) { 646 | child := Value{element, true} 647 | slice = append(slice, &child) 648 | } 649 | 650 | return slice, nil 651 | } 652 | 653 | return slice, ErrNotArray 654 | 655 | } 656 | 657 | // Attempts to typecast the current value into a number. 658 | // Returns error if the current value is not a json number. 659 | // Example: 660 | // ageNumber, err := ageValue.Number() 661 | func (v *Value) Number() (json.Number, error) { 662 | var valid bool 663 | 664 | // Check the type of this data 665 | switch v.data.(type) { 666 | case json.Number: 667 | valid = true 668 | break 669 | } 670 | 671 | if valid { 672 | return v.data.(json.Number), nil 673 | } 674 | 675 | return "", ErrNotNumber 676 | } 677 | 678 | // Attempts to typecast the current value into a float64. 679 | // Returns error if the current value is not a json number. 680 | // Example: 681 | // percentage, err := v.Float64() 682 | func (v *Value) Float64() (float64, error) { 683 | n, err := v.Number() 684 | 685 | if err != nil { 686 | return 0, err 687 | } 688 | 689 | return n.Float64() 690 | } 691 | 692 | // Attempts to typecast the current value into a int64. 693 | // Returns error if the current value is not a json number. 694 | // Example: 695 | // id, err := v.Int64() 696 | func (v *Value) Int64() (int64, error) { 697 | n, err := v.Number() 698 | 699 | if err != nil { 700 | return 0, err 701 | } 702 | 703 | return n.Int64() 704 | } 705 | 706 | // Attempts to typecast the current value into a bool. 707 | // Returns error if the current value is not a json boolean. 708 | // Example: 709 | // marriedBool, err := marriedValue.Boolean() 710 | func (v *Value) Boolean() (bool, error) { 711 | var valid bool 712 | 713 | // Check the type of this data 714 | switch v.data.(type) { 715 | case bool: 716 | valid = true 717 | break 718 | } 719 | 720 | if valid { 721 | return v.data.(bool), nil 722 | } 723 | 724 | return false, ErrNotBool 725 | } 726 | 727 | // Attempts to typecast the current value into an object. 728 | // Returns error if the current value is not a json object. 729 | // Example: 730 | // friendObject, err := friendValue.Object() 731 | func (v *Value) Object() (*Object, error) { 732 | 733 | var valid bool 734 | 735 | // Check the type of this data 736 | switch v.data.(type) { 737 | case map[string]interface{}: 738 | valid = true 739 | break 740 | } 741 | 742 | if valid { 743 | obj := new(Object) 744 | obj.valid = valid 745 | 746 | m := make(map[string]*Value) 747 | 748 | if valid { 749 | for key, element := range v.data.(map[string]interface{}) { 750 | m[key] = &Value{element, true} 751 | 752 | } 753 | } 754 | 755 | obj.data = v.data 756 | obj.m = m 757 | 758 | return obj, nil 759 | } 760 | 761 | return nil, ErrNotObject 762 | } 763 | 764 | // Attempts to typecast the current value into an object arrau. 765 | // Returns error if the current value is not an array of json objects 766 | // Example: 767 | // friendObjects, err := friendValues.ObjectArray() 768 | func (v *Value) ObjectArray() ([]*Object, error) { 769 | 770 | var valid bool 771 | 772 | // Check the type of this data 773 | switch v.data.(type) { 774 | case []interface{}: 775 | valid = true 776 | break 777 | } 778 | 779 | // Unsure if this is a good way to use slices, it's probably not 780 | var slice []*Object 781 | 782 | if valid { 783 | 784 | for _, element := range v.data.([]interface{}) { 785 | childValue := Value{element, true} 786 | childObject, err := childValue.Object() 787 | 788 | if err != nil { 789 | return nil, ErrNotObjectArray 790 | } 791 | slice = append(slice, childObject) 792 | } 793 | 794 | return slice, nil 795 | } 796 | 797 | return nil, ErrNotObjectArray 798 | 799 | } 800 | 801 | // Attempts to typecast the current value into a string. 802 | // Returns error if the current value is not a json string 803 | // Example: 804 | // nameObject, err := nameValue.String() 805 | func (v *Value) String() (string, error) { 806 | var valid bool 807 | 808 | // Check the type of this data 809 | switch v.data.(type) { 810 | case string: 811 | valid = true 812 | break 813 | } 814 | 815 | if valid { 816 | return v.data.(string), nil 817 | } 818 | 819 | return "", ErrNotString 820 | } 821 | 822 | // Returns the value a json formatted string. 823 | // Note: The method named String() is used by golang's log method for logging. 824 | // Example: 825 | func (v *Object) String() string { 826 | 827 | f, err := json.Marshal(v.data) 828 | if err != nil { 829 | return err.Error() 830 | } 831 | 832 | return string(f) 833 | 834 | } 835 | -------------------------------------------------------------------------------- /jason_test.go: -------------------------------------------------------------------------------- 1 | package jason 2 | 3 | import ( 4 | "log" 5 | "testing" 6 | ) 7 | 8 | type Assert struct { 9 | T *testing.T 10 | } 11 | 12 | func NewAssert(t *testing.T) *Assert { 13 | return &Assert{ 14 | T: t, 15 | } 16 | } 17 | 18 | func (assert *Assert) True(value bool, message string) { 19 | if value == false { 20 | log.Panicln("Assert: ", message) 21 | } 22 | } 23 | 24 | func TestFirst(t *testing.T) { 25 | 26 | assert := NewAssert(t) 27 | 28 | testJSON := `{ 29 | "name": "anton", 30 | "age": 29, 31 | "nothing": null, 32 | "true": true, 33 | "false": false, 34 | "list": [ 35 | "first", 36 | "second" 37 | ], 38 | "list2": [ 39 | { 40 | "street": "Street 42", 41 | "city": "Stockholm" 42 | }, 43 | { 44 | "street": "Street 42", 45 | "city": "Stockholm" 46 | } 47 | ], 48 | "address": { 49 | "street": "Street 42", 50 | "city": "Stockholm" 51 | }, 52 | "country": { 53 | "name": "Sweden" 54 | } 55 | }` 56 | 57 | j, err := NewObjectFromBytes([]byte(testJSON)) 58 | 59 | a, err := j.GetObject("address") 60 | assert.True(a != nil && err == nil, "failed to create json from string") 61 | 62 | assert.True(err == nil, "failed to create json from string") 63 | 64 | s, err := j.GetString("name") 65 | 66 | assert.True(s == "anton" && err == nil, "name should be a string") 67 | 68 | s, err = j.GetString("name") 69 | assert.True(s == "anton" && err == nil, "name shoud match") 70 | 71 | s, err = j.GetString("address", "street") 72 | assert.True(s == "Street 42" && err == nil, "street shoud match") 73 | //log.Println("s: ", s.String()) 74 | 75 | _, err = j.GetNumber("age") 76 | assert.True(err == nil, "age should be a number") 77 | 78 | n, err := j.GetInt64("age") 79 | assert.True(n == 29 && err == nil, "age mismatch") 80 | 81 | ageInterface, err := j.GetInterface("age") 82 | assert.True(ageInterface != nil, "should be defined") 83 | assert.True(err == nil, "age interface error") 84 | 85 | invalidInterface, err := j.GetInterface("not_existing") 86 | assert.True(invalidInterface == nil, "should not give error here") 87 | assert.True(err != nil, "should give error here") 88 | 89 | age, err := j.GetValue("age") 90 | assert.True(age != nil && err == nil, "age should exist") 91 | 92 | age2, err := j.GetValue("age2") 93 | assert.True(age2 == nil && err != nil, "age2 should not exist") 94 | 95 | address, err := j.GetObject("address") 96 | assert.True(address != nil && err == nil, "address should be an object") 97 | 98 | //log.Println("address: ", address) 99 | 100 | s, err = address.GetString("street") 101 | 102 | addressAsString, err := j.GetString("address") 103 | assert.True(addressAsString == "" && err != nil, "address should not be an string") 104 | 105 | s, err = j.GetString("address", "street") 106 | assert.True(s == "Street 42" && err == nil, "street mismatching") 107 | 108 | s, err = j.GetString("address", "name2") 109 | assert.True(s == "" && err != nil, "nonexistent string fail") 110 | 111 | b, err := j.GetBoolean("true") 112 | assert.True(b == true && err == nil, "bool true test") 113 | 114 | b, err = j.GetBoolean("false") 115 | assert.True(b == false && err == nil, "bool false test") 116 | 117 | b, err = j.GetBoolean("invalid_field") 118 | assert.True(b == false && err != nil, "bool invalid test") 119 | 120 | list, err := j.GetValueArray("list") 121 | assert.True(list != nil && err == nil, "list should be an array") 122 | 123 | list2, err := j.GetValueArray("list2") 124 | assert.True(list2 != nil && err == nil, "list2 should be an array") 125 | 126 | list2Array, err := j.GetValueArray("list2") 127 | assert.True(err == nil, "List2 should not return error on AsArray") 128 | assert.True(len(list2Array) == 2, "List2 should should have length 2") 129 | 130 | list2Value, err := j.GetValue("list2") 131 | assert.True(err == nil, "List2 should not return error on value") 132 | 133 | list2ObjectArray, err := list2Value.ObjectArray() 134 | assert.True(err == nil, "list2Value should not return error on ObjectArray") 135 | assert.True(len(list2ObjectArray) == 2, "list2ObjectArray should should have length 2") 136 | 137 | for _, elementValue := range list2Array { 138 | //assert.True(element.IsObject() == true, "first fail") 139 | 140 | element, err := elementValue.Object() 141 | 142 | s, err = element.GetString("street") 143 | assert.True(s == "Street 42" && err == nil, "second fail") 144 | } 145 | 146 | obj, err := j.GetObject("country") 147 | assert.True(obj != nil && err == nil, "country should not return error on AsObject") 148 | for key, value := range obj.Map() { 149 | 150 | assert.True(key == "name", "country name key incorrect") 151 | 152 | s, err = value.String() 153 | assert.True(s == "Sweden" && err == nil, "country name should be Sweden") 154 | } 155 | } 156 | 157 | func TestSecond(t *testing.T) { 158 | json := ` 159 | { 160 | "data": [ 161 | { 162 | "id": "X999_Y999", 163 | "from": { 164 | "name": "Tom Brady", "id": "X12" 165 | }, 166 | "message": "Looking forward to 2010!", 167 | "actions": [ 168 | { 169 | "name": "Comment", 170 | "link": "http://www.facebook.com/X999/posts/Y999" 171 | }, 172 | { 173 | "name": "Like", 174 | "link": "http://www.facebook.com/X999/posts/Y999" 175 | } 176 | ], 177 | "type": "status", 178 | "created_time": "2010-08-02T21:27:44+0000", 179 | "updated_time": "2010-08-02T21:27:44+0000" 180 | }, 181 | { 182 | "id": "X998_Y998", 183 | "from": { 184 | "name": "Peyton Manning", "id": "X18" 185 | }, 186 | "message": "Where's my contract?", 187 | "actions": [ 188 | { 189 | "name": "Comment", 190 | "link": "http://www.facebook.com/X998/posts/Y998" 191 | }, 192 | { 193 | "name": "Like", 194 | "link": "http://www.facebook.com/X998/posts/Y998" 195 | } 196 | ], 197 | "type": "status", 198 | "created_time": "2010-08-02T21:27:44+0000", 199 | "updated_time": "2010-08-02T21:27:44+0000" 200 | } 201 | ] 202 | }` 203 | 204 | assert := NewAssert(t) 205 | j, err := NewObjectFromBytes([]byte(json)) 206 | 207 | assert.True(j != nil && err == nil, "failed to parse json") 208 | 209 | dataObject, err := j.GetObject("data") 210 | assert.True(dataObject == nil && err != nil, "data should not be an object") 211 | 212 | dataArray, err := j.GetObjectArray("data") 213 | assert.True(dataArray != nil && err == nil, "data should be an object array") 214 | 215 | for index, dataItem := range dataArray { 216 | 217 | if index == 0 { 218 | id, err := dataItem.GetString("id") 219 | assert.True(id == "X999_Y999" && err == nil, "item id mismatch") 220 | 221 | fromName, err := dataItem.GetString("from", "name") 222 | assert.True(fromName == "Tom Brady" && err == nil, "fromName mismatch") 223 | 224 | actions, err := dataItem.GetObjectArray("actions") 225 | 226 | for index, action := range actions { 227 | 228 | if index == 1 { 229 | name, err := action.GetString("name") 230 | assert.True(name == "Like" && err == nil, "name mismatch") 231 | 232 | link, err := action.GetString("link") 233 | assert.True(link == "http://www.facebook.com/X999/posts/Y999" && err == nil, "Like mismatch") 234 | 235 | } 236 | 237 | } 238 | } else if index == 1 { 239 | id, err := dataItem.GetString("id") 240 | assert.True(id == "X998_Y998" && err == nil, "item id mismatch") 241 | } 242 | 243 | } 244 | 245 | } 246 | 247 | func TestErrors(t *testing.T) { 248 | json := ` 249 | { 250 | "string": "hello", 251 | "number": 1, 252 | "array": [1,2,3] 253 | }` 254 | 255 | errstr := "expected an error getting %s, but got '%s'" 256 | 257 | j, err := NewObjectFromBytes([]byte(json)) 258 | if err != nil { 259 | t.Fatal("failed to parse json") 260 | } 261 | 262 | if _, err = j.GetObject("string"); err != ErrNotObject { 263 | t.Errorf(errstr, "object", err) 264 | } 265 | 266 | if err = j.GetNull("string"); err != ErrNotNull { 267 | t.Errorf(errstr, "null", err) 268 | } 269 | 270 | if _, err = j.GetStringArray("string"); err != ErrNotArray { 271 | t.Errorf(errstr, "array", err) 272 | } 273 | 274 | if _, err = j.GetStringArray("array"); err != ErrNotString { 275 | t.Errorf(errstr, "string array", err) 276 | } 277 | 278 | if _, err = j.GetNumber("array"); err != ErrNotNumber { 279 | t.Errorf(errstr, "number", err) 280 | } 281 | 282 | if _, err = j.GetBoolean("array"); err != ErrNotBool { 283 | t.Errorf(errstr, "boolean", err) 284 | } 285 | 286 | if _, err = j.GetString("number"); err != ErrNotString { 287 | t.Errorf(errstr, "string", err) 288 | } 289 | 290 | _, err = j.GetString("not_found") 291 | if e, ok := err.(KeyNotFoundError); !ok { 292 | t.Errorf(errstr, "key not found error", e) 293 | } 294 | 295 | } 296 | --------------------------------------------------------------------------------