2

I am trying to Parse Json & Print Data and save in a CSV using GO language .

It is printing the desired 10 result per page with Println but couldn't parse JSON .

This is what I am doing

  1. Visitor visits studentresults.com/page=1 RollNo. & Marks for 10 students are displayed and it is also saved in a CSV [key.rollno & key.marks]

  2. Program Makes a request to studentsapimarks.com/api/rollno1,rollno2,rollno3/detail (appending all key.rollno that we got in step1) The Json response would be like

    [{"name":"James","Percentage":96.5,"Remarks":"VeryGood"}, 
    {"name":"William","Percentage":36.0,"Remarks":"Bad"}, 
    {"name":"Jacob","Percentage":63.0,"Remarks":"Average"}, 
    {"name":"Wilson","Percentage":69.3,"Remarks":"Average"}, 
    {"name":"Kurtson","Percentage":16.9,"Remarks":"VeryBad"}, 
    {"name":"Tom","Percentage":86.3,"Remarks":"Good"}]
    
  3. Extract Name & Percentage fields & save in a csv

Here is my snippet of the code that is responsible for printing and saving the results

package main

import (
    "encoding/csv"
    "fmt"
    "net/http"
    "os"
    "strings"
    "encoding/json"

)


type Key struct {
    fname      string
    marks      int
    rollno     int
    }

type object struct {
    Percentage float64
    Name string `json:"name"`
    }


func PageRequest(w http.ResponseWriter, r *http.Request) {


    // Page header
    fmt.Fprintf(w, PageHeader, pages, previous, next)

    // Save in csv
    csvfile, err := os.OpenFile("marks.csv", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer csvfile.Close()

    writer := csv.NewWriter(csvfile)
    defer writer.Flush()

    // Defining RollNos
    var rollNos []string

    // Marks for UID
    UID, length := compute(start)
    for i := 0; i < length; i++ {
        key := UID[i]

    // Prints firstname, Marks and Rollno
        fmt.Fprintf(w, key.fname, key.marks, key.rollno) 



        records := [][]string{{key.fname, key.marks, key.rollno}}

        for _, record := range records {
            err := writer.Write(record)
            if err != nil {
                fmt.Println("Error:", err)
                return
            }
        }

    // Append All key.rollno
        rollNos := append(rollNos, key.rollno)

    // Makes a request to as studentsapimarks.com/api/rollno1,rollno2,rollno3/detail
        uri := "https://studentsapimarks.com/api/" + strings.Join(rollNos, ",") + "/detail"

        res, err := http.Get(uri)
        if err != nil {
            log.Fatal(err)
        }
        defer res.Body.Close()

        var s []object
        err = json.NewDecoder(res.Body).Decode(&s)
        if err != nil {
            log.Fatal(err)
        }

    // Prints Name/ Percentage
        fmt.Println(s[0].Name)
        fmt.Println(s[0].Percentage)
    }

    // Page Footer
    fmt.Fprintf(w, PageFooter, previous, next)
}


func main() {
    http.HandleFunc("/", PageRequest)

log.Println("Listening")
log.Fatal(http.ListenAndServe(":8080", nil))

}

fmt.Fprintf(w, key.fname, key.marks, key.rollno) is working fine and displays all 10 results per page and saves in a csv .

Problem with the code is

  1. It is not appending all key.rollno that we got in step1 as studentsapimarks.com/api/rollno1,rollno2,rollno3/detail

  2. Also how could i save the JSON extracted s[0].name results in a CSV ?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Nancyme
  • 43
  • 7

1 Answers1

1

The JSON parsing is failing because you're trying to unmarshal a JSON array into a struct. You want to unmarshal a JSON array into a slice - the outer struct is not necessary:

var s []object
err = json.NewDecoder(res.Body).Decode(&s)

Also, it can only unmarshal exported fields (those that start with a capital letter), so to get the name, you'll need to change your struct definition. I'm guessing the percentage isn't coming through either, since in the JSON it's a single float value, but in your struct you're taking a float slice.

type object struct {
    // "Percentage": 96.4 is not a slice.
    // Also if the field name in Go and JSON are identical, you dont need the json tag.
    Percentage float64
    // The field must be capitalized, the tag can stay lowercase
    Name string `json:"name"`
}

Then you'll access those fields accordingly:

// Prints Name/ Percentage
fmt.Println(s[0].Name)
fmt.Println(s[0].Percentage)
Adrian
  • 42,911
  • 6
  • 107
  • 99
  • removed `type object struct` & `type ObjectsAPIResponse struct` and used `var s []object` now getting error `undefined: object` – Nancyme Jul 07 '17 at 15:25
  • That's because you removed the object type. I gave you an updated definition for it - don't remove it, use the updated definition. – Adrian Jul 07 '17 at 15:27
  • now error `s.Allmarks undefined (type []object has no field or method Allmarks)` – Nancyme Jul 07 '17 at 15:28
  • Updated answer. – Adrian Jul 07 '17 at 15:38
  • removed `type ObjectsAPIResponse struct` and used `var s []object` and modified `fmt.Println(s.Allmarks[0].name)` to `fmt.Println(s.[0].name)` – Nancyme Jul 07 '17 at 15:39
  • now error `http: panic serving [::1]:49193: runtime error: index out of range` bla bla – Nancyme Jul 07 '17 at 15:41
  • That's a completely separate issue you'll need to troubleshoot. – Adrian Jul 07 '17 at 15:42
  • maybe because i am using it under for loop? similar issue `https://stackoverflow.com/questions/39118941/go-panic-runtime-error-index-out-of-range-but-length-of-array-is-not-null` – Nancyme Jul 07 '17 at 15:44
  • or if the response is empty will it go `index out of range` ? – Nancyme Jul 07 '17 at 15:51
  • Yes, if the response is empty and you try to access an element, you'll get index out of range - accessing the first element out of zero elements is out of range. – Adrian Jul 07 '17 at 15:52
  • ok,whats the solution for it, because it can be that the response is empty so it case it should ignore it and carry on with the program? – Nancyme Jul 07 '17 at 15:54
  • Check the length first. – Adrian Jul 07 '17 at 16:20