diff --git a/README.md b/README.md index 1506a45..889761a 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ func defaultUsage(){ // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. diff --git a/connect.go b/connect.go index 7bc1bb9..22d69e9 100644 --- a/connect.go +++ b/connect.go @@ -24,6 +24,8 @@ type connect struct { workbookRelsIDMap map[string]string // xl/workbook.xml workbookFile *zip.File + // map["sheet_id"]"sheet_name" + worksheetIDToNameMap map[string]string // "xl/worksheets/sheet*.xml" // map["xl/path/to/sheet*.xml"]*zip.File worksheetFileMap map[string]*zip.File @@ -99,8 +101,9 @@ func (conn *connect) Close() error { // NewReader generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. -// if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. -// otherwise, will use sheetNamer as struct and reflect for it's name. +// if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] +// if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. +// otherwise, will use sheetNamer as struct and reflect for it's name. func (conn *connect) NewReader(sheetNamer interface{}) (Reader, error) { return conn.NewReaderByConfig(&Config{Sheet: sheetNamer}) } @@ -119,7 +122,7 @@ func (conn *connect) NewReaderByConfig(config *Config) (Reader, error) { if conn.zipReader == nil { return nil, ErrConnectNotOpened } - sheet := parseSheetName(config.Sheet) + sheet := conn.parseSheetName(config.Sheet) sheet = config.Prefix + sheet + config.Suffix workSheetFile, ok := conn.worksheetNameFileMap[sheet] if !ok { @@ -246,7 +249,8 @@ func (conn *connect) readWorkbook() error { if conn.sheets == nil { conn.sheets = make([]string, 0, len(wb.Sheets.Sheet)) } - conn.worksheetNameFileMap = make(map[string]*zip.File) + conn.worksheetNameFileMap = make(map[string]*zip.File, len(wb.Sheets.Sheet)) + conn.worksheetIDToNameMap = make(map[string]string, len(wb.Sheets.Sheet)) for _, sheet := range wb.Sheets.Sheet { conn.sheets = append(conn.sheets, sheet.Name) // record the sheet name to *zip.File @@ -261,6 +265,7 @@ func (conn *connect) readWorkbook() error { } // log.Println(sheet.Name) conn.worksheetNameFileMap[sheet.Name] = file + conn.worksheetIDToNameMap[sheet.SheetID] = sheet.Name } rc.Close() return nil diff --git a/standard_example_test.go b/standard_example_test.go index 58a0e7a..8f3f9ed 100644 --- a/standard_example_test.go +++ b/standard_example_test.go @@ -37,6 +37,7 @@ func ExampleReader_readStruct() { // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -78,6 +79,7 @@ func ExampleReader_readAllSliceStruct() { var stdList []Standard // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -131,6 +133,7 @@ func ExampleReader_readAllSlicePtr() { var stdList []*Standard // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -174,6 +177,7 @@ func ExampleReader_readBinaryAllSlicePtr() { var stdList []*Standard // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -210,6 +214,7 @@ func ExampleReader_readMap() { // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -252,6 +257,7 @@ func ExampleReader_readAllSliceMap() { // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -290,6 +296,7 @@ func ExampleReader_readAllSliceMapOtherValueType() { // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. diff --git a/standard_test.go b/standard_test.go index 16a4ff6..e5e5c41 100644 --- a/standard_test.go +++ b/standard_test.go @@ -187,6 +187,7 @@ func TestReadStandard(t *testing.T) { // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will use sheet as sheet name. // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -209,6 +210,40 @@ func TestReadStandard(t *testing.T) { } } +func TestReadStandardIndex(t *testing.T) { + conn := excel.NewConnecter() + err := conn.Open(filePath) + if err != nil { + t.Error(err) + return + } + defer conn.Close() + + // Generate an new reader of a sheet + // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will use sheet as sheet name. + // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. + // otherwise, will use sheetNamer as struct and reflect for it's name. + // if sheetNamer is a slice, the type of element will be used to infer like before. + rd, err := conn.NewReader(2) + if err != nil { + t.Error(err) + return + } + defer rd.Close() + + idx := 0 + for rd.Next() { + var s Standard + rd.Read(&s) + expectStd := expectStandardList[idx] + if !reflect.DeepEqual(s, expectStd) { + t.Errorf("unexpect std at %d = \n%s", idx, convert.MustJsonPrettyString(expectStd)) + } + idx++ + } +} + func TestReadStandardAll(t *testing.T) { conn := excel.NewConnecter() err := conn.Open(filePath) @@ -221,6 +256,7 @@ func TestReadStandardAll(t *testing.T) { var stdList []Standard // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -265,6 +301,7 @@ func TestReadStandardPtrAll(t *testing.T) { var stdList []*Standard // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -303,6 +340,7 @@ func TestReadBinaryStandardPtrAll(t *testing.T) { var stdList []*Standard // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -334,6 +372,7 @@ func TestReadStandardMap(t *testing.T) { // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. @@ -368,6 +407,7 @@ func TestReadStandardSliceMap(t *testing.T) { // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. diff --git a/types.go b/types.go index d1661c7..8b3f51d 100644 --- a/types.go +++ b/types.go @@ -45,12 +45,14 @@ type Connecter interface { // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. NewReader(sheetNamer interface{}) (Reader, error) // Generate an new reader of a sheet // sheetNamer: if sheetNamer is string, will use sheet as sheet name. + // if sheetNamer is int, will i'th sheet in the workbook, be careful the hidden sheet is counted. i ∈ [1,+inf] // if sheetNamer is a object implements `GetXLSXSheetName()string`, the return value will be used. // otherwise, will use sheetNamer as struct and reflect for it's name. // if sheetNamer is a slice, the type of element will be used to infer like before. diff --git a/utils.go b/utils.go index 969649a..bde7e08 100644 --- a/utils.go +++ b/utils.go @@ -1,13 +1,19 @@ package excel import ( + "fmt" "reflect" ) -func parseSheetName(i interface{}) string { +func (conn *connect) parseSheetName(i interface{}) string { switch s := i.(type) { case string: return s + case int, int8, int32, int64, uint, uint8, uint16, uint32, uint64: + if name, ok := conn.worksheetIDToNameMap[fmt.Sprintf("%d", s)]; ok { + return name + } + return "" case interface { GetXLSXSheetName() string }: @@ -21,7 +27,7 @@ func parseSheetName(i interface{}) string { if typ.Kind() == reflect.Ptr { typ = typ.Elem() } - return parseSheetName(reflect.New(typ).Elem().Interface()) + return conn.parseSheetName(reflect.New(typ).Elem().Interface()) default: return typ.Name() } diff --git a/xml_workbook.go b/xml_workbook.go index b28687f..e06505b 100644 --- a/xml_workbook.go +++ b/xml_workbook.go @@ -59,6 +59,7 @@ type xlsxSheets struct { // xlsxSheet directly maps the sheet element from the namespace // http://schemas.openxmlformats.org/spreadsheetml/2006/main type xlsxSheet struct { - Name string `xml:"name,attr,omitempty"` - RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` + SheetID string `xml:"sheetId,attr,omitempty"` + RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"` }