diff --git a/scanner/scanner.go b/scanner/scanner.go index 9f8e8bf..750b471 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -26,6 +26,7 @@ type Scanner interface { ReadFloat64(target *float64) error ReadBool(target *bool) error ReadMap(target *map[string]interface{}) error + ReadArray(target *[]interface{}) error } type scanner struct { @@ -581,8 +582,12 @@ func (s *scanner) ReadMap(target *map[string]interface{}) error { } v[key] = m case TLBRACKET: - // TODO: Read arrays. - panic("Not supported yet") + s.Unscan(tok, b) + var arr []interface{} + if err := s.ReadArray(&arr); err != nil { + return err + } + v[key] = arr default: return fmt.Errorf("Unexpected %s at %d: %s", TokenName(tok), s.Pos(), string(b)) } @@ -592,3 +597,62 @@ func (s *scanner) ReadMap(target *map[string]interface{}) error { return nil } + +func (s *scanner) ReadArray(target *[]interface{}) error { + if tok, b, err := s.Scan(); err != nil { + return err + } else if tok != TLBRACKET { + return fmt.Errorf("Unexpected %s at %d: %s; expected '['", TokenName(tok), s.Pos(), string(b)) + } + + index := 0 + for { + tok, b, err := s.Scan() + if err != nil { + return err + } else if tok == TRBRACKET { + return nil + } else if tok == TCOMMA { + if index == 0 { + return fmt.Errorf("Unexpected comma in array at %d", s.Pos()) + } + if tok, b, err = s.Scan(); err != nil { + return err + } + } + + var v interface{} + switch tok { + case TSTRING: + v = string(b) + case TNUMBER: + v, _ = strconv.ParseFloat(string(b), 64) + case TTRUE: + v = true + case TFALSE: + v = false + case TNULL: + v = nil + case TLBRACE: + s.Unscan(tok, b) + m := make(map[string]interface{}) + if err := s.ReadMap(&m); err != nil { + return err + } + v = m + case TLBRACKET: + s.Unscan(tok, b) + var arr []interface{} + if err := s.ReadArray(&arr); err != nil { + return err + } + v = arr + default: + return fmt.Errorf("Unexpected %s at %d: %s", TokenName(tok), s.Pos(), string(b)) + } + *target = append(*target, v) + + index++ + } + return nil +} diff --git a/scanner/scanner_test.go b/scanner/scanner_test.go index 49d3420..d20b84a 100644 --- a/scanner/scanner_test.go +++ b/scanner/scanner_test.go @@ -218,6 +218,17 @@ func TestReadMap(t *testing.T) { assert.Equal(t, nested["xxx"], "yyy") } +// Ensures that a map with arrays can be read into a field. +func TestReadMapWithArray(t *testing.T) { + var v map[string]interface{} + err := NewScanner(strings.NewReader(`{"foo":["bar", 42]}`)).ReadMap(&v) + assert.NoError(t, err) + arr := v["foo"].([]interface{}) + t.Logf("got=%#v", v) + assert.Equal(t, "bar", arr[0].(string)) + assert.Equal(t, 42.0, arr[1].(float64)) +} + func BenchmarkScanNumber(b *testing.B) { withBuffer(b, "100", func(buf []byte) { s := NewScanner(bytes.NewBuffer(buf))