Read a CSV line by line and unmarshal it to a struct - csv

I have a 8 gigs CSV file that i need to unmarshal to a list of struct
package main
import (
"encoding/csv"
"fmt"
"io"
"os"
gocsv "github.com/gocarina/gocsv"
dto "github.com/toto/GeoTransport/import/dto"
)
// Put in parameter json the csv names
func importAdresse() {
var adressesDB []dto.GeoAdresse
clientsFile, err := os.OpenFile("../../../data/geo/public.geo_adresse.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
panic(err)
}
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
r := csv.NewReader(in)
r.Comma = ';'
return r // Allows use pipe as delimiter
})
if err = gocsv.UnmarshalFile(clientsFile, &adressesDB); err != nil { // Load clients from file
panic(err)
}
var i int
i = 0
for _, adresse := range adressesDB {
fmt.Println("adresse.Numero")
fmt.Printf("%+v\n", adresse)
fmt.Println(adresse.Numero)
i++
if i == 3 {
break
}
}
}
func init() {
}
func main() {
importAdresse()
}
Actually I am using go csv to unmarshall it but I have some memory error.
The program quit because it does not have enough ram.
I would like to know how to read the csv line by line and unmarshal it to a struct.
One of the solution will be to split the CSV file with some unix command.
But I would like to know how to do it with only Go.

It looks like the parsing method you're using attempts to read the entire CSV file into memory. You might try using the standard CSV reader package directly, or using another CSV-to-struct library that allows for line-by-line decoding like this one. Does the example code on those pages show what you're looking for?
Another thing to try would be running wc -l ../../../data/geo/public.geo_adresse.csv to get the number of lines in your CSV file, then write this:
var adressesDB [<number of lines in your CSV>]dto.GeoAdresse
If the runtime raises the out of memory exception on that line, it means that the unmarshalled CSV data exceeds your RAM capacity and you'll have to read it in chunks.

Related

How to read "interfaces" map of json without defining structure in Golang?

Following this tutorial I'm trying to read a json file in Golang. It says there are two ways of doing that:
unmarshal the JSON using a set of predefined structs
or unmarshal the JSON using a map[string]interface{}
Since I'll probably have a lot of different json formats I prefer to interpret it on the fly. So I now have the following code:
package main
import (
"fmt"
"os"
"io/ioutil"
"encoding/json"
)
func main() {
// Open our jsonFile
jsonFile, err := os.Open("users.json")
// if we os.Open returns an error then handle it
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened users.json")
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var result map[string]interface{}
json.Unmarshal([]byte(byteValue), &result)
fmt.Println(result["users"])
fmt.Printf("%T\n", result["users"])
}
This prints out:
Successfully Opened users.json
[map[type:Reader age:23 social:map[facebook:https://facebook.com twitter:https://twitter.com] name:Elliot] map[name:Fraser type:Author age:17 social:map[facebook:https://facebook.com twitter:https://twitter.com]]]
[]interface {}
At this point I don't understand how I can read the age of the first user (23). I tried some variations:
fmt.Println(result["users"][0])
fmt.Println(result["users"][0].age)
But apparently, type interface {} does not support indexing.
Is there a way that I can access the items in the json without defining the structure?
Probably you want
fmt.Println(result["users"].(map[string]interface{})["age"])
or
fmt.Println(result[0].(map[string]interface{})["age"])
As the JSON is a map of maps the type of the leaf nodes is interface{} and so has to be converted to map[string]interface{} in order to lookup a key
Defining a struct is much easier. My top tip for doing this is to use a website that converts JSON to a Go struct definition, like Json-To-Go

Parsing nested JSON objects in a CSV file with golang

I'm trying to parse a CSV file which contains a JSON object in the last column.
Here is an example with two rows from the input CSV file:
'id','value','createddate','attributes'
524256,CAFE,2018-04-06 16:41:01,{"Att1Numeric": 6, "Att2String": "abc"}
524257,BEBE,2018-04-06 17:00:00,{}
I tried using the parser from csv package:
func processFileAsCSV(f *multipart.Part) (int, error) {
reader := csv.NewReader(f)
reader.LazyQuotes = true
reader.Comma = ','
lineCount := 0
for {
line, err := reader.Read()
if err == io.EOF {
break
} else if err != nil {
fmt.Println("Error:", err)
return 0, err
}
if lineCount%100000 == 0 {
fmt.Println(lineCount)
}
lineCount++
fmt.Println(lineCount, line)
processLine(line) // do something with the line
}
fmt.Println("done!", lineCount)
return lineCount, nil
}
But I got an error:
Error: line 2, column 0: wrong number of fields in line,
probably because the parser ignores the JSON scope which starts with {.
Should I be writing my own CSV parser, or is there a library that can handle this?
Your CSV input doesn't follow normal CSV convention, by using unquoted fields (for JSON).
I think the best approach would be to pre-process your input, either in your Go program, or in an external script.
If your CSV input is predictable (as indicated in your question), it should be easy to properly quote last element, using a simple strings.Split call, for instance, before passing it to the CSV parser.

Properly Create a JSON File and Read from It

I am trying to create a JSON file with Golang. I have little knowledge of JSON Files and creating them but I have created a program that creates them. In this program, it takes form data from a website and then puts the data into a JSON struct, then adds the information into a folder. I have the 2 sections of data here. I put a comment where the error occurs along with the error
{
"cl":"[v1]",
"gr":"[A]",
"cr":"[8]"
} // End Of File Expected
{
"cl":"[v2]",
"gr":"[Z]",
"cr":"[8]"
}
So my questions are (1) What does the error mean, and (2) How/can I fix this when creating a JSON file with Golang? I can supply the Golang if needed.
So other than the json not being formatted correctly, here is an example of how to create json using a struct and json struct tags.
Proper JSON
[
{key:value, key value},
{key:value, key value}
]
What you have is
{key:value, key value}
{key:value, key value}
Which is two separate objects instead of one object in an array.
If you are reading this from file and the data is returned like your example then you might have to split on the newline to separate each object and unmarshal them separately.
Otherwise the below should server as an example.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"strconv"
)
type j struct {
Cl []string `json:"cl"`
Gr []string `json:"gr"`
Cr []string `json:"cr"`
}
func main() {
// create an instance of j as a slice
var data []j
// using a for loop for create dummy data fast
for i := 0; i < 5; i++ {
v := strconv.Itoa(i)
data = append(data, j{
Cl: []string{"foo " + v},
Gr: []string{"bar " + v},
Cr: []string{"foobar " + v},
})
}
// printing out json neatly to demonstrate
b, _ := json.MarshalIndent(data, "", " ")
fmt.Println(string(b))
// writing json to file
_ = ioutil.WriteFile("file.json", b, 0644)
// to append to a file
// create the file if it doesn't exists with O_CREATE, Set the file up for read write, add the append flag and set the permission
f, err := os.OpenFile("/var/log/debug-web.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
if err != nil {
log.Fatal(err)
}
// write to file, f.Write()
f.Write(b)
// if you are doing alot of I/O work you may not want to write out to file often instead load up a bytes.Buffer and write to file when you are done... assuming you don't run out of memory when loading to bytes.Buffer
}

Output a simple json file to a rest api with golang

This is my first go project. All I want to do is read a file.json on my server, then make it available to others via a REST API. But I'm getting errors. Here's what I have so far.
main.go
package main
import (
"encoding/json"
"github.com/gorilla/mux"
"log"
"net/http"
"io/ioutil"
"fmt"
)
func GetDetail(w http.ResponseWriter, r *http.Request) {
b,_ := ioutil.ReadFile("file.json");
rawIn := json.RawMessage(string(b))
var objmap map[string]*json.RawMessage
err := json.Unmarshal(rawIn, &objmap)
if err != nil {
fmt.Println(err)
}
fmt.Println(objmap)
json.NewEncoder(w).Encode(objmap)
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/detail", GetDetail).Methods("GET")
log.Fatal(http.ListenAndServe(":8000", router))
}
file.json
{
favourite_color:"blue",
attribute:{density:23,allergy:"peanuts",locations:["USA","Canada","Jamaica"]},
manufacture_year:1998
}
When I run go build; ./sampleproject, then go to my web browser at http://localhost:8000/detail, I get the error message:
invalid character 'f' looking for beginning of object key string
map[]
I've tried a few marshal methods, but they all give me different errors. I just need a working example to study from to better understand how all this works.
I should also mention that file.json does not have a fixed schema. It can change drastically at any minute to have a random set of data.
How do I get around the error invalid character f message and get my file.json to render at http://localhost:8000/detail?
As cerise mentioned, it's just formatting error in the JSON file. Must quote all properties.
Then the rest of the code works

Golang: Type [type] is not an expression; json config parsing

I'm trying to work out a bit of code to pull in config from a JSON file.
When I attempt to build, I get this error
type ConfigVars is not an expression
Below is the config and program code I'm trying to work with. Every example I've found so far is similar to the below code. Any suggestion of what I'm doing incorrectly?
-- Config File
{"beaconUrl":"http://test.com/?id=1"}
-- Program Code
package main
import (
"encoding/json"
"fmt"
"os"
)
type ConfigVars struct {
BeaconUrl string
}
func main() {
configFile, err := os.Open("config.json")
defer configFile.Close()
if err != nil {
fmt.Println("Opening config file", err.Error())
}
jsonParser := json.NewDecoder(configFile)
if err = jsonParser.Decode(&ConfigVars); err != nil {
fmt.Println("Parsing config file", err.Error())
}
}
What you're doing there is trying to pass a pointer to the ConfigVars type (which obviously doesn't really mean anything). What you want to do is make a variable whose type is ConfigVars and pass a pointer to that instead:
var cfg ConfigVars
err = jsonParser.Decode(&cfg)
...
For others who come onto this problem, you may find that you've forgotten to initialize the variable during assignment using the := operator, as described in Point 3 at the end of this GoLang tutorial.
var cfg ConfigVars
err := jsonParser.Decode(&cfg)