├── .travis.yml ├── README.md ├── example_test.go ├── scan.go └── scan_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - tip 4 | before_install: 5 | - go get github.com/mattn/goveralls 6 | - go get golang.org/x/tools/cmd/cover 7 | script: 8 | - $HOME/gopath/bin/goveralls -repotoken Tj40FXTQLbNcT9jTy3COPuXHeGaNm4znk 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-scan 2 | ======= 3 | 4 | [![Build Status](https://travis-ci.org/mattn/go-scan.png?branch=master)](https://travis-ci.org/mattn/go-scan) 5 | [![Coverage Status](https://coveralls.io/repos/mattn/go-scan/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-scan?branch=HEAD) 6 | 7 | Easily way to get the elements via xpath like string 8 | 9 | Usage 10 | ----- 11 | 12 | ```go 13 | var js = strings.NewReader(` 14 | { 15 | "foo": { 16 | "bar": [ 17 | { 18 | "faz": true, 19 | "moo": ["goo", "mar"] 20 | }, 21 | { 22 | "maz": true, 23 | "moo": ["foo", "bar"] 24 | } 25 | ] 26 | } 27 | } 28 | `) 29 | var s []string 30 | scan.ScanJSON(js, "/foo/bar[1]/moo", &s) // s should be ["foo", "bar"] 31 | ``` 32 | 33 | Install 34 | ------- 35 | 36 | ``` 37 | go get github.com/mattn/go-scan 38 | ``` 39 | 40 | License 41 | ------- 42 | 43 | MIT: http://mattn.mit-license.org/2013 44 | 45 | Author 46 | ------ 47 | 48 | Yasuhiro Matsumoto (mattn.jp@gmail.com) 49 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package scan_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mattn/go-scan" 6 | "strings" 7 | ) 8 | 9 | var js = strings.NewReader(` 10 | { 11 | "foo": { 12 | "bar": [ 13 | { 14 | "baz": "bbb", 15 | "noo": 3 16 | }, 17 | { 18 | "maz": true, 19 | "moo": ["foo", "bar"] 20 | } 21 | ], 22 | "boo": { 23 | "bag": "ddd", 24 | "bug": "ccc" 25 | } 26 | } 27 | } 28 | `) 29 | 30 | func Example() { 31 | var s []string 32 | if err := scan.ScanJSON(js, "/foo/bar[1]/moo", &s); err != nil { 33 | println(err.Error()) 34 | } 35 | fmt.Println(s[0]) // should be "foo" 36 | fmt.Println(s[1]) // should be "bar" 37 | } 38 | -------------------------------------------------------------------------------- /scan.go: -------------------------------------------------------------------------------- 1 | package scan 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | "reflect" 8 | "regexp" 9 | "strconv" 10 | ) 11 | 12 | var re = regexp.MustCompile("^([^\\[]+)?(\\[[0-9]+\\])?$") 13 | 14 | var t1 = reflect.TypeOf((map[string]interface{})(nil)) 15 | var t2 = reflect.TypeOf((map[interface{}]interface{})(nil)) 16 | 17 | // Any provide interface to scan any types. 18 | type Any interface{} 19 | 20 | func toError(v interface{}) error { 21 | if v != nil { 22 | if e, ok := v.(error); ok { 23 | return e 24 | } 25 | if e, ok := v.(string); ok { 26 | return errors.New(e) 27 | } 28 | return errors.New("Unknown error") 29 | } 30 | return nil 31 | } 32 | 33 | func isNillable(v reflect.Value) bool { 34 | switch v.Kind() { 35 | case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, 36 | reflect.UnsafePointer, reflect.Interface, reflect.Slice: 37 | return true 38 | default: 39 | return false 40 | } 41 | } 42 | 43 | // Scan work to scan any type to specified type 44 | func Scan(v interface{}, t interface{}) (err error) { 45 | defer func() { 46 | if err == nil { 47 | err = toError(recover()) 48 | } 49 | }() 50 | rt := reflect.ValueOf(t).Elem() 51 | rv := reflect.ValueOf(v) 52 | if v == nil || (isNillable(rv) && rv.IsNil()) { 53 | if !isNillable(rt) { 54 | return errors.New("nil is not assignable") 55 | } 56 | rt.Set(reflect.Zero(rt.Type())) 57 | return nil 58 | } 59 | tv := rv.Type().Kind() 60 | 61 | if tv == reflect.Slice || tv == reflect.Array { 62 | if _, ok := t.(*Any); ok { 63 | rt.Set(rv) 64 | return nil 65 | } 66 | ia := rv.Interface().([]interface{}) 67 | rt.Set(reflect.MakeSlice(rt.Type(), len(ia), len(ia))) 68 | for n := range ia { 69 | rt.Index(n).Set(rv.Index(n).Elem()) 70 | } 71 | } else { 72 | if rv.Type().ConvertibleTo(rt.Type()) { 73 | rt.Set(rv.Convert(rt.Type())) 74 | } else { 75 | rt.Set(rv) 76 | } 77 | } 78 | return nil 79 | } 80 | 81 | func split(s string) []string { 82 | i := 0 83 | a := []string{} 84 | t := "" 85 | rs := []rune(s) 86 | l := len(rs) 87 | for i < l { 88 | r := rs[i] 89 | switch r { 90 | case '\\': 91 | i++ 92 | if i < l { 93 | t += string(rs[i]) 94 | } 95 | case '/': 96 | if t != "" { 97 | a = append(a, t) 98 | t = "" 99 | } 100 | default: 101 | t += string(r) 102 | } 103 | i++ 104 | } 105 | if t != "" { 106 | a = append(a, t) 107 | } 108 | return a 109 | } 110 | 111 | // ScanTree work to scan value to specified value with the path 112 | func ScanTree(v interface{}, p string, t interface{}) (err error) { 113 | defer func() { 114 | if err == nil { 115 | err = toError(recover()) 116 | } 117 | }() 118 | if p == "" { 119 | return errors.New("invalid path") 120 | } 121 | var ok bool 122 | for _, token := range split(p) { 123 | sl := re.FindAllStringSubmatch(token, -1) 124 | if len(sl) == 0 { 125 | return errors.New("invalid path") 126 | } 127 | ss := sl[0] 128 | if ss[1] != "" { 129 | rv := reflect.ValueOf(v) 130 | rt := rv.Type() 131 | if rt != t1 && rv.Type().ConvertibleTo(t1) { 132 | v = rv.Convert(t1).Interface() 133 | } 134 | if vm, ok := v.(map[string]interface{}); ok { 135 | if v, ok = vm[ss[1]]; !ok { 136 | return errors.New("invalid path: " + ss[1]) 137 | } 138 | } else { 139 | if rt != t2 && rv.Type().ConvertibleTo(t2) { 140 | v = rv.Convert(t2).Interface() 141 | } 142 | if vm, ok := v.(map[interface{}]interface{}); ok { 143 | if v, ok = vm[ss[1]]; !ok { 144 | return errors.New("invalid path: " + ss[1]) 145 | } 146 | } else { 147 | return errors.New("invalid path: " + ss[1]) 148 | } 149 | } 150 | } 151 | if ss[2] != "" { 152 | i, err := strconv.Atoi(ss[2][1 : len(ss[2])-1]) 153 | if err != nil { 154 | return errors.New("invalid path: " + ss[2]) 155 | } 156 | var vl []interface{} 157 | if vl, ok = v.([]interface{}); !ok { 158 | if vm, ok := v.(map[string]interface{}); ok { 159 | n, found := 0, false 160 | for _, vv := range vm { 161 | if n == i { 162 | found = true 163 | v = vv 164 | break 165 | } 166 | n++ 167 | } 168 | if !found { 169 | return errors.New("invalid path: " + ss[2]) 170 | } 171 | } else if vm, ok := v.(map[interface{}]interface{}); ok { 172 | n, found := 0, false 173 | for _, vv := range vm { 174 | if n == i { 175 | found = true 176 | v = vv 177 | break 178 | } 179 | n++ 180 | } 181 | if !found { 182 | return errors.New("invalid path: " + ss[2]) 183 | } 184 | } else { 185 | return errors.New("invalid path: " + ss[2]) 186 | } 187 | } else { 188 | if i < 0 || i > len(vl)-1 { 189 | return errors.New("invalid path: " + ss[2]) 190 | } 191 | v = vl[i] 192 | } 193 | } 194 | } 195 | return Scan(v, t) 196 | } 197 | 198 | // ScanJSON work as same sa ScanTree. it allow to give Reader. 199 | func ScanJSON(r io.Reader, p string, t interface{}) (err error) { 200 | defer func() { 201 | if err == nil { 202 | err = toError(recover()) 203 | } 204 | }() 205 | var a interface{} 206 | if err = json.NewDecoder(r).Decode(&a); err != nil { 207 | return err 208 | } 209 | return ScanTree(a, p, t) 210 | } 211 | -------------------------------------------------------------------------------- /scan_test.go: -------------------------------------------------------------------------------- 1 | package scan 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | var ScanTests = []struct { 11 | j string 12 | p string 13 | v interface{} 14 | }{ 15 | { 16 | "foo", 17 | "bar", 18 | "baz", 19 | }, 20 | } 21 | 22 | func TestScanString(t *testing.T) { 23 | var a interface{} 24 | j := 25 | ` 26 | "foo" 27 | ` 28 | err := json.Unmarshal([]byte(j), &a) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | var s string 33 | err = Scan(a, &s) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | if s != "foo" { 38 | t.Fatalf("Expected %v for Scan, but %v:", "foo", s) 39 | } 40 | } 41 | 42 | func TestScanBool(t *testing.T) { 43 | var a interface{} 44 | j := 45 | ` 46 | true 47 | ` 48 | err := json.Unmarshal([]byte(j), &a) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | var b bool 53 | err = Scan(a, &b) 54 | if err != nil { 55 | t.Fatal(err) 56 | } 57 | if !b { 58 | t.Fatalf("Expected %v for Scan, but %v:", true, b) 59 | } 60 | } 61 | func TestScanFloat64(t *testing.T) { 62 | var a interface{} 63 | j := 64 | ` 65 | 123 66 | ` 67 | err := json.Unmarshal([]byte(j), &a) 68 | if err != nil { 69 | t.Fatal(err) 70 | } 71 | var f float64 72 | err = Scan(a, &f) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | if f != 123.0 { 77 | t.Fatalf("Expected %f for Scan, but %f:", 123.0, f) 78 | } 79 | } 80 | 81 | func TestScanTreeMap(t *testing.T) { 82 | var a interface{} 83 | j := 84 | ` 85 | {"foo":{"bar": "baz"}} 86 | ` 87 | err := json.Unmarshal([]byte(j), &a) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | var s string 92 | err = ScanTree(a, "/foo/bar", &s) 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | if s != "baz" { 97 | t.Fatalf("Expected %v for Scan, but %v:", "baz", s) 98 | } 99 | } 100 | 101 | func TestScanTreeStringKeyMap(t *testing.T) { 102 | var a map[string]interface{} 103 | j := 104 | ` 105 | {"foo":{"bar": "baz"}} 106 | ` 107 | err := json.Unmarshal([]byte(j), &a) 108 | if err != nil { 109 | t.Fatal(err) 110 | } 111 | var s string 112 | err = ScanTree(a, "/foo/bar", &s) 113 | if err != nil { 114 | t.Fatal(err) 115 | } 116 | if s != "baz" { 117 | t.Fatalf("Expected %v for Scan, but %v:", "baz", s) 118 | } 119 | } 120 | 121 | func TestScanTreeInterfaceKeyMap(t *testing.T) { 122 | a := map[interface{}]interface{}{ 123 | "foo": map[string]interface{}{ 124 | "bar": "baz", 125 | }, 126 | 3: "baba", 127 | } 128 | var s string 129 | err := ScanTree(a, "/foo/bar", &s) 130 | if err != nil { 131 | t.Fatal(err) 132 | } 133 | if s != "baz" { 134 | t.Fatalf("Expected %v for Scan, but %v:", "baz", s) 135 | } 136 | } 137 | 138 | func TestScanTreeInvalidKeyMap(t *testing.T) { 139 | a := map[interface{}]interface{}{ 140 | "foo": map[string]interface{}{ 141 | "bar": "bar", 142 | }, 143 | 3: "baba", 144 | } 145 | var s string 146 | err := ScanTree(a, "/foo/baz", &s) 147 | if err == nil { 148 | t.Fatal("Expected error but not") 149 | } 150 | } 151 | 152 | func TestScanTreeInvalidMap(t *testing.T) { 153 | a := map[interface{}]interface{}{ 154 | "foo": map[interface{}]interface{}{ 155 | "bar": func() {}, 156 | "barbar": func() {}, 157 | }, 158 | 3: "baba", 159 | } 160 | var s string 161 | err := ScanTree(a, "/foo[0]", &s) 162 | if err == nil { 163 | t.Fatal("Expected error but not") 164 | } 165 | } 166 | 167 | func TestScanTreeSliceOfString(t *testing.T) { 168 | var a interface{} 169 | j := 170 | ` 171 | {"foo":{"bar": ["baz", "baba"]}} 172 | ` 173 | err := json.Unmarshal([]byte(j), &a) 174 | if err != nil { 175 | t.Fatal(err) 176 | } 177 | var s []string 178 | err = ScanTree(a, "/foo/bar", &s) 179 | if err != nil { 180 | t.Fatal(err) 181 | } 182 | if !reflect.DeepEqual([]string{"baz", "baba"}, s) { 183 | t.Fatalf("Expected %v for Scan, but %v:", `["bar": "baba"]`, s) 184 | } 185 | } 186 | 187 | func TestScanTreeSliceOfFloat64(t *testing.T) { 188 | var a interface{} 189 | j := 190 | ` 191 | {"foo":{"bar": [3, 2, 1]}} 192 | ` 193 | err := json.Unmarshal([]byte(j), &a) 194 | if err != nil { 195 | t.Fatal(err) 196 | } 197 | var f []float64 198 | err = ScanTree(a, "/foo/bar", &f) 199 | if err != nil { 200 | t.Fatal(err) 201 | } 202 | if !reflect.DeepEqual([]float64{3, 2, 1}, f) { 203 | t.Fatalf("Expected %v for Scan, but %v:", `[3, 2, 1]`, f) 204 | } 205 | } 206 | 207 | func TestScanAny(t *testing.T) { 208 | s := `{"foo":{"bar": [3, 2, 1]}}` 209 | var a Any 210 | err := ScanJSON(strings.NewReader(s), "/foo/bar", &a) 211 | if err != nil { 212 | t.Fatal(err) 213 | } 214 | var v interface{} 215 | v = interface{}([]interface{}{3.0, 2.0, 1.0}) 216 | if !reflect.DeepEqual(v, a) { 217 | t.Fatalf("Expected %v for Scan, but %v:", `[3, 2, 1]`, a) 218 | } 219 | } 220 | 221 | func TestScanJSON(t *testing.T) { 222 | s := `{"foo":{"bar": [3, 2, 1]}}` 223 | var f []float64 224 | err := ScanJSON(strings.NewReader(s), "/foo/bar", &f) 225 | if err != nil { 226 | t.Fatal(err) 227 | } 228 | if !reflect.DeepEqual([]float64{3, 2, 1}, f) { 229 | t.Fatalf("Expected %v for Scan, but %v:", `[3, 2, 1]`, f) 230 | } 231 | var i int 232 | err = ScanJSON(strings.NewReader(s), "/foo/bar[2]", &i) 233 | if err != nil { 234 | t.Fatal(err) 235 | } 236 | if i != 1 { 237 | t.Fatalf("Expected %v for Scan, but %v:", 1, i) 238 | } 239 | } 240 | 241 | func TestScanPanic(t *testing.T) { 242 | var b bool 243 | err := Scan(nil, &b) 244 | if err == nil { 245 | t.Fatal("Expected error but not") 246 | } 247 | } 248 | 249 | func TestScanJSONError(t *testing.T) { 250 | err := ScanJSON(nil, "", nil) 251 | if err == nil { 252 | t.Fatal("Expected error but not") 253 | } 254 | sr := strings.NewReader("") 255 | err = ScanJSON(sr, "/", nil) 256 | if err == nil { 257 | t.Fatal("Expected error but not") 258 | } 259 | } 260 | 261 | func TestIndexWithMap(t *testing.T) { 262 | var js = ` 263 | { 264 | "foo": { 265 | "bar": [ 266 | { 267 | "baz": "bbb", 268 | "noo": 3 269 | }, 270 | { 271 | "maz": true, 272 | "moo": ["foo", "bar"] 273 | } 274 | ], 275 | "boo": { 276 | "bag": "ddd", 277 | "bug": "ccc" 278 | } 279 | } 280 | } 281 | ` 282 | var a interface{} 283 | err := json.Unmarshal([]byte(js), &a) 284 | if err != nil { 285 | t.Fatal(err) 286 | } 287 | var s string 288 | if err := ScanTree(a, "/foo/boo[0]", &s); err != nil { 289 | if err := ScanTree(a, "/foo/boo[1]", &s); err != nil { 290 | t.Fatal(err) 291 | } 292 | } 293 | } 294 | 295 | func TestScan(t *testing.T) { 296 | var f float32 297 | err := Scan(nil, &f) 298 | if err == nil { 299 | t.Fatal("Expected error but not") 300 | } 301 | } 302 | 303 | func TestInvalidPath(t *testing.T) { 304 | err := Scan(nil, nil) 305 | if err == nil { 306 | t.Fatal("Expected error but not") 307 | } 308 | err = ScanTree(nil, "", nil) 309 | if err == nil { 310 | t.Fatal("Expected error but not") 311 | } 312 | err = ScanJSON(nil, "", nil) 313 | if err == nil { 314 | t.Fatal("Expected error but not") 315 | } 316 | err = ScanTree(nil, "a", nil) 317 | if err == nil { 318 | t.Fatal("Expected error but not") 319 | } 320 | err = ScanJSON(nil, "a", nil) 321 | if err == nil { 322 | t.Fatal("Expected error but not") 323 | } 324 | err = ScanTree(nil, "/[a]", nil) 325 | if err == nil { 326 | t.Fatal("Expected error but not") 327 | } 328 | err = ScanJSON(nil, "/[a]", nil) 329 | if err == nil { 330 | t.Fatal("Expected error but not") 331 | } 332 | err = ScanTree(nil, "/a", nil) 333 | if err == nil { 334 | t.Fatal("Expected error but not") 335 | } 336 | err = ScanJSON(nil, "/a", nil) 337 | if err == nil { 338 | t.Fatal("Expected error but not") 339 | } 340 | 341 | s := `{"foo":{"bar": [3, 2, 1]}}` 342 | var f []float64 343 | err = ScanJSON(strings.NewReader(s), "/fooo/bar", &f) 344 | if err == nil { 345 | t.Fatal("Expected error but not") 346 | } 347 | err = ScanJSON(strings.NewReader(s), "/foo[999999999999999999999999999999999999999]", &f) 348 | if err == nil { 349 | t.Fatal("Expected error but not") 350 | } 351 | err = ScanJSON(strings.NewReader(s), "/foo/bar[0]/[0]", &f) 352 | if err == nil { 353 | t.Fatal("Expected error but not") 354 | } 355 | err = ScanJSON(strings.NewReader(s), "/foo/bar[20]", &f) 356 | if err == nil { 357 | t.Fatal("Expected error but not") 358 | } 359 | err = ScanJSON(strings.NewReader(s), "/foo[9]", &f) 360 | if err == nil { 361 | t.Fatal("Expected error but not") 362 | } 363 | err = ScanJSON(strings.NewReader(s), "[9]", nil) 364 | if err == nil { 365 | t.Fatal("Expected error but not") 366 | } 367 | } 368 | 369 | func TestToError(t *testing.T) { 370 | err := toError(1) 371 | if err == nil { 372 | t.Fatal("Expected error but not") 373 | } 374 | if err.Error() != "Unknown error" { 375 | t.Fatal("Expected unknown error but not") 376 | } 377 | if toError(nil) != nil { 378 | t.Fatal("Expected nil error but not") 379 | } 380 | } 381 | 382 | func TestSlash(t *testing.T) { 383 | s := `{"foo bar":{"bar/baz": [3, 2, 1]}}` 384 | var f []float64 385 | err := ScanJSON(strings.NewReader(s), `/foo bar/bar\/baz`, &f) 386 | if err != nil { 387 | t.Fatal(err) 388 | } 389 | if !reflect.DeepEqual([]float64{3, 2, 1}, f) { 390 | t.Fatalf("Expected %v for Scan, but %v:", `[3, 2, 1]`, f) 391 | } 392 | var i int 393 | err = ScanJSON(strings.NewReader(s), `/foo bar/bar\/baz[2]`, &i) 394 | if err != nil { 395 | t.Fatal(err) 396 | } 397 | if i != 1 { 398 | t.Fatalf("Expected %v for Scan, but %v:", 1, i) 399 | } 400 | } 401 | 402 | func TestScanNull(t *testing.T) { 403 | s := `{"foo":null}` 404 | scan := func(i interface{}) error { 405 | return ScanJSON(strings.NewReader(s), "/foo", i) 406 | } 407 | 408 | a := []interface{}{} 409 | err := scan(&a) 410 | if err != nil { 411 | t.Error(err) 412 | } else if a != nil { 413 | t.Error("Expected an array to be nil") 414 | } 415 | 416 | m := map[string]interface{}{} 417 | err = scan(&m) 418 | if err != nil { 419 | t.Error(err) 420 | } else if m != nil { 421 | t.Error("Expected a map to be nil") 422 | } 423 | 424 | var any Any 425 | err = scan(&any) 426 | if err != nil { 427 | t.Error(err) 428 | } else if any != nil { 429 | t.Error("Expected an Any to be nil") 430 | } 431 | } 432 | --------------------------------------------------------------------------------