csv

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2022 License: GPL-3.0 Imports: 6 Imported by: 0

README

go-csv

This project defines a csv struct decorator tag, and wraper for the base csv package that can understand and use the tag in order to more easily parse csv data.

To use, import this module into your code with "github.com/AidanJHMurphy/go-csv"

How to decorate a struct with the csv tag

If your csv file uses a header, define the mapping with the header attribute. For example, given the following struct definition:

type csvWithHeader struct {
	Field1       string `csv:"header:field1Header"`
	Field2       int    `csv:"header:field2Header"`
	Field3       int    `csv:"header:field3Header"`
}

... and the folling csv-formatted string

`field1Header,ignoredColumn,field3Header,field2Header
value1,thisIsUnusedData,3,2`

... we would retrieve the following data

data := []csvWithHeader [
  {
    Field1: "value1",
    Field2: 2,
    Field3: 3,
  },
]

If your csv file does not use a header, define the mapping with the index attribute. The index is a zero-indexed integer. For example, given the following struct definition:

type csvWithIndex struct {
	Field1 string `csv:"index:0"`
	Field2 int    `csv:"index:3"`
}

... and the folling csv-formatted string

`10,11,12,13,14`

... we would retrieve the following data

data := []csvWithIndex [
  {
    Field1: 10,
    Field2: 13,
  },
]

Fields in a struct that don't have the csv tag applied will be skipped over.

type ignoredField struct {
  ThisFieldWontBeWrittenTo string
  ThisFieldWill `csv:"index:0"`
}

Fields must be exported for the csv tag to work. The following struct definition is invalid:

type improperlyAppliedTag struct {
  unexportedField `csv:"index:0"`
}

CSV tags must defined either the header, or the index attribute to be valid. The following struct definition is invalid:

type improperlyDefinedTag struct {
  InvalidTag `csv:""`
}

If you are setting data that needs additional handling beyond the default, or you are setting a data type that isn't supported, implement the CustomSetter interface for your struct. For example, given the following struct definition:

type implementsCustomSetter struct {
  CustomField1 string `csv:"index:0;useCustomSetter"`
  CustomField2 string `csv:"index:0;useCustomSetter"`
}

func (isc *implementsCustomSetter) CustomSetter(fieldName string, value string) (err error) {
  if fieldName = "CustomField1" {
    isc.CustomField1 = strings.ToUpper(value)
    return nil
  }
  
  if fieldName = "CustomField2" {
    isc.CustomField2 = ToLower(value)
    return nil
  }
  
  return fmt.Errorf("custom setter called for unexpected field")
}

... and the folling csv-formatted string

`hErE Is SoMe wOnKeY DaTa
hErE Is SoMe mOrE WoNkEy dAtA`

... we would retrieve the following data

data := []implementsCustomSetter [
  {
    CustomField1: "HERE IS SOME WONKEY DATA",
    CustomField2: "here is some wonkey data",
  },
  {
    CustomField1: "HERE IS SOME MORE WONKEY DATA",
    CustomField2: "here is some more wonkey data",
  },
]

How to parse csv data

Once you have defined a struct with csv tags, you'll need to create a new csv parser for the file you want to parse. Then, if your data uses headers, parse the header. Once you have done that, read the csv data into your struct.

Here is an example with headers:

package main

import (
	"fmt"
	"io"
	"strings"

	csv "github.com/AidanJHMurphy/go-csv"
)

const csvWithHeaderData = `field1Header,ignoredColumn,field3Header,field2Header
value1,thisIsUnusedData,3,2`

type csvWithHeader struct {
	Field1 string `csv:"header:field1Header"`
	Field2 int    `csv:"header:field2Header"`
	Field3 int    `csv:"header:field3Header"`
}

func main() {
	p := csv.NewParser(strings.NewReader(csvWithHeaderData), csv.ParserOptions{})

	err := p.ParseHeader(&csvWithHeader{})
	if err != nil {
		fmt.Printf("encountered error parsing csv header: %v", err)
	}

	for {
		data := csvWithHeader{}
		err := p.ReadRecord(&data)
		if err == io.EOF {
			break
		}

		if err != nil {
			fmt.Printf("encountered error parsing csv without header: %v", err)
			break
		}

		fmt.Printf("%v\n", data)
	}
}

Here is an example without headers:

package main

import (
	"fmt"
	"io"
	"strings"

	csv "github.com/AidanJHMurphy/go-csv"
)

const csvWithoutHeaderData = `10,11,12,13,14
20,21,22,23,24`

type csvWithoutHeader struct {
	Field1 string `csv:"index:0"`
	Field2 int    `csv:"index:3"`
}

func main() {
	p := csv.NewParser(strings.NewReader(csvWithoutHeaderData), csv.ParserOptions{})

	for {
		data := csvWithoutHeader{}
		err := p.ReadRecord(&data)
		if err == io.EOF {
			break
		}

		if err != nil {
			fmt.Printf("encountered error parsing csv with header: %v", err)
			break
		}

		fmt.Printf("%v\n", data)
	}
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrorMissingCustomSetter = fmt.Errorf("cannot use custom data type without implementing CustomSetter interface")
	ErrorUnsupportedDataType = fmt.Errorf("must implement CustomSetter interface when using unsupported data types")
	ErrorInvalidIndex        = fmt.Errorf("index must be a non negative integer")
	ErrorMalformedCsvTag     = fmt.Errorf("you need to specify either the header or index")
	ErrorUnexportedField     = fmt.Errorf("csv tags may not be set on unexported fields")
	ErrorFieldNotFound       = fmt.Errorf("field not found in header")
)

Functions

This section is empty.

Types

type CsvTagDefError

type CsvTagDefError struct {
	CsvTag    string
	FieldName string
	Err       error
}

func (CsvTagDefError) Error

func (e CsvTagDefError) Error() string

func (CsvTagDefError) Unwrap

func (e CsvTagDefError) Unwrap() error

type CustomSetter

type CustomSetter interface {
	CustomSetter(fieldName string, value string) (err error)
}

type FieldNotFoundError

type FieldNotFoundError struct {
	FieldName  string
	HeaderName string
	Err        error
}

func (FieldNotFoundError) Error

func (e FieldNotFoundError) Error() string

func (FieldNotFoundError) Unwrap

func (e FieldNotFoundError) Unwrap() error

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

func NewParser

func NewParser(file io.Reader, options ParserOptions) (p Parser)

func (*Parser) ParseHeader

func (p *Parser) ParseHeader(structPointer interface{}) (err error)

func (*Parser) ReadRecord

func (p *Parser) ReadRecord(structPointer interface{}) (err error)

type ParserOptions

type ParserOptions struct {
	Delimiter rune
}

type SetValueError

type SetValueError struct {
	Value     string
	FieldName string
	Err       error
}

func (SetValueError) Error

func (e SetValueError) Error() string

func (SetValueError) Unwrap

func (e SetValueError) Unwrap() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL