Working with XML and CSV in Go
XML and CSV are common data formats used in many applications. Go provides excellent support for both through the standard library. This guide covers parsing, encoding, and practical techniques for working with these formats.
Working with CSV
Reading CSV Files
package main
import (
"encoding/csv"
"fmt"
"log"
"os"
)
func main() {
// Open CSV file
file, err := os.Open("data.csv")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Create CSV reader
reader := csv.NewReader(file)
// Read all records
records, err := reader.ReadAll()
if err != nil {
log.Fatal(err)
}
// Process records
for i, record := range records {
fmt.Printf("Row %d: %v\n", i, record)
}
}
Reading CSV Line by Line
package main
import (
"encoding/csv"
"fmt"
"log"
"os"
)
func main() {
file, err := os.Open("data.csv")
if err != nil {
log.Fatal(err)
}
defer file.Close()
reader := csv.NewReader(file)
// Read line by line
for {
record, err := reader.Read()
if err != nil {
break
}
fmt.Printf("Name: %s, Age: %s, Email: %s\n", record[0], record[1], record[2])
}
}
Writing CSV Files
package main
import (
"encoding/csv"
"log"
"os"
)
func main() {
// Create CSV file
file, err := os.Create("output.csv")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Create CSV writer
writer := csv.NewWriter(file)
defer writer.Flush()
// Write header
writer.Write([]string{"Name", "Age", "Email"})
// Write records
writer.Write([]string{"Alice", "30", "[email protected]"})
writer.Write([]string{"Bob", "25", "[email protected]"})
writer.Write([]string{"Charlie", "35", "[email protected]"})
}
CSV with Custom Delimiter
package main
import (
"encoding/csv"
"fmt"
"log"
"os"
"strings"
)
func main() {
// CSV with semicolon delimiter
data := "Name;Age;Email\nAlice;30;[email protected]\nBob;25;[email protected]"
reader := csv.NewReader(strings.NewReader(data))
reader.Comma = ';'
records, err := reader.ReadAll()
if err != nil {
log.Fatal(err)
}
for _, record := range records {
fmt.Println(record)
}
}
Parsing CSV into Structs
package main
import (
"encoding/csv"
"fmt"
"log"
"os"
"strconv"
)
type Person struct {
Name string
Age int
Email string
}
func main() {
file, err := os.Open("data.csv")
if err != nil {
log.Fatal(err)
}
defer file.Close()
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
log.Fatal(err)
}
var people []Person
// Skip header
for _, record := range records[1:] {
age, _ := strconv.Atoi(record[1])
person := Person{
Name: record[0],
Age: age,
Email: record[2],
}
people = append(people, person)
}
for _, p := range people {
fmt.Printf("%+v\n", p)
}
}
Working with XML
Parsing XML
package main
import (
"encoding/xml"
"fmt"
"log"
)
type Book struct {
Title string `xml:"title"`
Author string `xml:"author"`
Year int `xml:"year"`
}
func main() {
xmlData := `
<book>
<title>Go Programming</title>
<author>John Doe</author>
<year>2023</year>
</book>
`
var book Book
err := xml.Unmarshal([]byte(xmlData), &book)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", book)
}
Parsing XML with Attributes
package main
import (
"encoding/xml"
"fmt"
"log"
)
type Person struct {
Name string `xml:"name,attr"`
Age int `xml:"age,attr"`
}
type Company struct {
Name string `xml:"name"`
Persons []Person `xml:"person"`
}
func main() {
xmlData := `
<company>
<name>TechCorp</name>
<person name="Alice" age="30"/>
<person name="Bob" age="25"/>
</company>
`
var company Company
err := xml.Unmarshal([]byte(xmlData), &company)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", company)
for _, p := range company.Persons {
fmt.Printf(" %+v\n", p)
}
}
Encoding to XML
package main
import (
"encoding/xml"
"fmt"
"log"
)
type Book struct {
Title string `xml:"title"`
Author string `xml:"author"`
Year int `xml:"year"`
}
func main() {
book := Book{
Title: "Go Programming",
Author: "John Doe",
Year: 2023,
}
// Marshal to XML
data, err := xml.MarshalIndent(book, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Println(xml.Header + string(data))
}
Parsing XML from File
package main
import (
"encoding/xml"
"fmt"
"log"
"os"
)
type Library struct {
Books []Book `xml:"book"`
}
type Book struct {
Title string `xml:"title"`
Author string `xml:"author"`
}
func main() {
file, err := os.Open("library.xml")
if err != nil {
log.Fatal(err)
}
defer file.Close()
var library Library
decoder := xml.NewDecoder(file)
err = decoder.Decode(&library)
if err != nil {
log.Fatal(err)
}
for _, book := range library.Books {
fmt.Printf("%s by %s\n", book.Title, book.Author)
}
}
Practical Examples
CSV to JSON Conversion
package main
import (
"encoding/csv"
"encoding/json"
"fmt"
"log"
"os"
)
func main() {
// Read CSV
file, err := os.Open("data.csv")
if err != nil {
log.Fatal(err)
}
defer file.Close()
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
log.Fatal(err)
}
// Convert to JSON
var data []map[string]string
headers := records[0]
for _, record := range records[1:] {
row := make(map[string]string)
for i, header := range headers {
row[header] = record[i]
}
data = append(data, row)
}
// Output JSON
jsonData, _ := json.MarshalIndent(data, "", " ")
fmt.Println(string(jsonData))
}
XML to CSV Conversion
package main
import (
"encoding/csv"
"encoding/xml"
"log"
"os"
)
type Person struct {
Name string `xml:"name"`
Email string `xml:"email"`
}
type People struct {
Persons []Person `xml:"person"`
}
func main() {
// Read XML
file, err := os.Open("people.xml")
if err != nil {
log.Fatal(err)
}
defer file.Close()
var people People
decoder := xml.NewDecoder(file)
decoder.Decode(&people)
// Write CSV
outFile, _ := os.Create("output.csv")
defer outFile.Close()
writer := csv.NewWriter(outFile)
defer writer.Flush()
writer.Write([]string{"Name", "Email"})
for _, p := range people.Persons {
writer.Write([]string{p.Name, p.Email})
}
}
Data Validation
package main
import (
"encoding/csv"
"fmt"
"log"
"os"
"strconv"
"strings"
)
func validateRecord(record []string) error {
if len(record) != 3 {
return fmt.Errorf("invalid record length")
}
// Validate name
if strings.TrimSpace(record[0]) == "" {
return fmt.Errorf("name cannot be empty")
}
// Validate age
age, err := strconv.Atoi(record[1])
if err != nil || age < 0 || age > 150 {
return fmt.Errorf("invalid age")
}
// Validate email
if !strings.Contains(record[2], "@") {
return fmt.Errorf("invalid email")
}
return nil
}
func main() {
file, err := os.Open("data.csv")
if err != nil {
log.Fatal(err)
}
defer file.Close()
reader := csv.NewReader(file)
records, _ := reader.ReadAll()
for i, record := range records[1:] {
if err := validateRecord(record); err != nil {
fmt.Printf("Row %d: %v\n", i+2, err)
}
}
}
Best Practices
โ Good Practices
// Always close files
file, _ := os.Open("data.csv")
defer file.Close()
// Handle errors
records, err := reader.ReadAll()
if err != nil {
log.Fatal(err)
}
// Use struct tags for mapping
type Person struct {
Name string `xml:"name" csv:"name"`
}
// Validate data
if err := validateRecord(record); err != nil {
// Handle error
}
// Use appropriate delimiters
reader.Comma = ';'
โ Anti-Patterns
// Don't ignore errors
records, _ := reader.ReadAll()
// Don't forget to close files
file, _ := os.Open("data.csv")
// Don't assume data format
// Always validate
// Don't use hardcoded indices
// Use struct mapping instead
Resources
- Go encoding/csv Documentation
- Go encoding/xml Documentation
- CSV Format Specification
- XML Specification
Summary
Go provides excellent support for CSV and XML:
- Use
encoding/csvfor CSV data - Use
encoding/xmlfor XML data - Map data to structs using tags
- Always validate data
- Handle errors properly
- Close files with defer
With these tools, you can efficiently work with structured data formats in Go.
Comments