How to convert numerical value to json in GoLang - go

I have a Go container that calculates the total marks scored, and works perfectly.
For this to be able to be read by my proxy file, i need the return value to be json.
I am trying this however it isnt working:
// Find the total grade
marksSum := 0
for _, mark := range marks {
marksSum += mark
}
j, _ := json.Marshal(markSum)
return j
Any help is much appreciated!

You can create a struct of how you want to structure your JSON object.(variable names should start with a capital letters)
type Response struct {
Error error `json:"error"`
Input_text string `json:"string"`
Answer int `json:"answer"`
}
Then just create a response using the above struct.
func main() {
marks := []int{1, 2, 3, 4}
marksSum := 0
input := ""
for _, mark := range marks {
input = fmt.Sprintf("%s %d", input, mark)
marksSum += mark
}
resp := &Response{
Error: nil,
Input_text: input,
Answer: marksSum,
}
j, err := json.Marshal(resp)
if err != nil {
fmt.Printf("Errr : %v", err)
return
}
fmt.Println(string(j))
}
https://go.dev/play/p/iC484GS7GKS

Related

Golang parse array

I am trying to figure out why my code is not working. I wish to take a slice of numbers and strings, and separate it into three slices. For each element in the slice, if it is a string, append it to the strings slice, and if it is a positive number, append it to the positive numbers, and likewise with negative. Yet, here is the output
Names:
EvTremblay
45.39934611083154
-75.71148292845268
[Crestview -75.73795670904249
BellevueManor -75.73886856878032
Dutchie'sHole -75.66809864107668 ...
Positives:[45.344387632924054 45.37223315413918 ... ]
Negatives: []
Here is my code. Can someone tell me what is causing the Negatives array to not have any values?
func main() {
fmt.Printf("%q\n", strings.Split("a,b,c", ","))
var names []string
var positives, negatives []float64
bs, err := ioutil.ReadFile("poolss.txt")
if err != nil {
return
}
str := string(bs)
fmt.Println(str)
tokens := strings.Split(str, ",")
for _, token := range tokens {
if num, err := strconv.ParseFloat(token, 64); err == nil {
if num > 0 {
positives = append(positives, num)
} else {
negatives = append(negatives, num)
}
} else {
names = append(names, token)
}
fmt.Println(token)
}
fmt.Println(fmt.Sprintf("Strings: %v",names))
fmt.Println(fmt.Sprintf("Positives: %v", positives))
fmt.Println(fmt.Sprintf("Negatives: %v",negatives))
for i := range names{
fmt.Println(names[i])
fmt.Println(positives[i])
fmt.Println(negatives[i])
}
}
Your code has strings as a variable name:
var strings []string
and strings as a package name:
tokens := strings.Split(str, ",")
Don't do that!
strings.Split undefined (type []string has no field or method Split)
Playground: https://play.golang.org/p/HfZGj0jOT-P
Your problem above I think lies with the extra \n attached to each float probably - you get no negative entries if you end in a linefeed or you would get one if you have no linefeed at the end. So insert a printf so that you can see the errors you're getting from strconv.ParseFloat and all will become clear.
Some small points which may help:
Check errors, and don't depend on an error to be of only one type (this is what is confusing you here) - always print the error if it arrives, particularly when debugging
Don't use the name of a package for a variable (strings), it won't end well
Use a datastructure which reflects your data
Use the CSV package to read CSV data
So for example for storing the data you might want:
type Place struct {
Name string
Latitude int64
Longitude int64
}
Then read the data into that, depending on the fact that cols are in a given order, and store it in a []Place.
Here's what I tried, it works now! Thanks for the help, everyone!
func main() {
findRoute("poolss.csv", 5)
}
func findRoute( filename string, num int) []Edge {
var route []Edge
csvFile, err := os.Open(filename)
if err != nil {
return route
}
reader := csv.NewReader(bufio.NewReader(csvFile))
var pools []Pool
for {
line, error := reader.Read()
if error == io.EOF {
break
} else if error != nil {
log.Fatal(error)
}
lat, err := strconv.ParseFloat(line[1], 64)
long, err := strconv.ParseFloat(line[2], 64)
if err == nil {
pools = append(pools, Pool{
name: line[0],
latitude: lat,
longitude: long,
})
}
}
return route
}

golang - converting [ ]Interface to [ ]strings or joined string

How can I convert []interface to []strings or just as a joined single string with all elements in []interface ? Below is the screenshot showing exact value of "scope" which of type []interface with length of 2. In below code, where case is "slice" Currently i am doing this using reflect but this is not looking good. Wondering there should be some good way to join a slice\array of interface elements.
I also did try using json unmarshall like this "json.Unmarshal([]byte(jwtResp),&mymap)" but having trouble with type issues in converting jwt.MapClaims to byte[]
parsedkey, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(key))
parsedToken, jwtErr := jwt.Parse(bearerToken, func(token *jwt.Token) (interface{}, error) {
return parsedkey, nil
})
jwtValues = make(map[string]string)
// we dont know which data types we are dealing with here, so using swtich case based on value data type
jwtResp := parsedToken.Claims.(jwt.MapClaims)
for k, v := range jwtResp {
switch reflect.TypeOf(v).Kind() {
case reflect.Slice:
fmt.Println("tp is : ", reflect.TypeOf(v)) // this is []interface{}
fmt.Println("type is : ", reflect.TypeOf(v).Kind()) // this is slice
s := reflect.ValueOf(v)
for i := 0; i < s.Len(); i++ {
jwtValues[k] += s.Index(i).Interface().(string)
jwtValues[k] += "|"
}
case reflect.String:
jwtValues[k] = v.(string)
default:
fmt.Println("unknown datatype")
}
}
Thanks for the suggestions . Below is the closest solution i found replacing 'switch' with 'if' condition along with type assertions. 'reflect' is costly. Using below code, i am finding if values of jwtResp is a slice, or string or something else. If slice, traverse through the values([]interface with string type elements) and join those as one concatenated string.
for k, v := range jwtResp {
if s, ok := v.(string); ok {
JwtValues[k] = s
} else if s, ok := v.([]interface{}); ok {
sslice := make([]string, len(s))
for i, v := range s {
sslice[i] = v.(string)
}
JwtValues[k] = strings.Join(sslice, "|")
} else {
logger.Log(util.LogDebug, "unknown data type")
}
}
Not sure to have fully understood your question, but it seems you're on the right track.
parts := make([]string, s.Len())
for i := 0; i < s.Len(); i++ {
parts = append(parts, s.Index(i).String())
}
jwtValues[k] = strings.Join(parts, "|")

How can I output CSV with martini?

I would like to print CSV-data to the output with martini. Currently, I have always used r.JSON(200, somestruct) where r is a render.Render from github.com/martini-contrib.
Now I have an slice of structs and I would like to print them as CSV (stringify each field of a single struct and print one struct at one line).
Currently, I do it like this:
r.Data(200, []byte("id,Latitude,Longitude\n"))
for _, packet := range tour.Packets {
r.Data(200, []byte(strconv.FormatInt(packet.Id, 10)+","+strconv.FormatFloat(packet.Latitude, 'f', 6, 64)+","+strconv.FormatFloat(packet.Longitude, 'f', 6, 64)+"\n"))
}
But I don't like the way I do it for the following reasons:
It is downloaded directly and not printed to the screen.
I get http: multiple response.WriteHeader calls
I would prefer not to make this manually (the struct has much more fields, but all fields are either ìnt64, float64 or time.Time.
How can I implement the CSV export option in a simpler way?
Use the standard library. There is no general solution without reflection, but you can simplify it.
func handler(rw http.ResponseWriter) {
rw.Header().Add("Content-Type", "text/csv")
wr := csv.NewWriter(rw)
err := wr.Write([]string{"id", "Latitude", "Longitude"})
if err != nil {
...
}
for _, packet := range tour.Packets {
err := wr.Write([]string{
strconv.FormatInt(packet.Id, 10),
strconv.FormatFloat(packet.Latitude, 'f', 6, 64),
strconv.FormatFloat(packet.Longitude, 'f', 6, 64),
})
if err != nil {
...
}
}
}
If you need a general solution for any struct, it will require reflect.
See here.
// structToStringSlice takes a struct value and
// creates a string slice of all the values in that struct
func structToStringSlice(i interface{}) []string {
v := reflect.ValueOf(i)
n := v.NumField()
out := make([]string, n)
for i := 0; i < n; i++ {
field := v.Field(i)
switch field.Kind() {
case reflect.String:
out[i] = field.String()
case reflect.Int:
out[i] = strconv.FormatInt(field.Int(), 10)
// add cases here to support more field types.
}
}
return out
}
// writeToCSV prints a slice of structs as csv to a writer
func writeToCSV(w io.Writer, i interface{}) {
wr := csv.NewWriter(w)
v := reflect.ValueOf(i)
// Get slice's element type (some unknown struct type)
typ := v.Type().Elem()
numFields := typ.NumField()
fieldSet := make([]string, numFields)
for i := 0; i < numFields; i++ {
fieldSet[i] = typ.Field(i).Name
}
// Write header row
wr.Write(fieldSet)
// Write data rows
sliceLen := v.Len()
for i := 0; i < sliceLen; i++ {
wr.Write(structToStringSlice(v.Index(i).Interface()))
}
wr.Flush()
}
so then your example is just:
func handler(rw http.ResponseWriter) {
....
writeToCSV(rw, tour.Packets)
}
The function I've written will only work for int or string fields. You can easily extend this to more types by adding cases to the switch in structToStringSlice. See here for reflect docs on the other Kinds.

Golang read data from file and indicate a missing value

I am parsing a csv file which contains integer values, some of them might be missing:
1,2,3
1,2,
1,2,3
In my code I'm populating a struct with the data:
type Line struct {
One *int
Two *int
Three *int
}
My current guess to handle missing values would be to use a pointer to an int in order to show whether the value is missing:
// if nil, then no value in file
l := &Line{}
// read and parse...
l.Three = nil
However, using this approach makes assingment to *int cumbersome:
l := &Line{}
// NOT POSSIBLE, NOT COMPILING (cannot use 5 (type int) as type *int in assignment)
l.One = 5
// FEELS WRONG, BUT WORKS
tmpInt := 5
l.One = &tmpInt
How to handle missing integer values?
You could use a function to build your Line{} from a []string, a simple example:
func NewLine(s []string) (l *Line) {
fmt.Println(len(s))
if len(s) < 2 {
return
}
l = &Line{}
if i, err := strconv.Atoi(s[0]); err == nil {
l.One = &i
}
if i, err := strconv.Atoi(s[1]); err == nil {
l.Two = &i
}
if len(s) == 3 {
if i, err := strconv.Atoi(s[2]); err == nil {
l.Three = &i
}
}
return
}

Reading bytes into structs using reflection

I'm trying to write functions that will allow me to marshal/unmarshal simple structs into byte arrays. I've succeeded in writing Marshal, with help from the kind folks at #go-nuts, but I'm running into trouble writing Unmarshal.
// Unmarshal unpacks the binary data and stores it in the packet using
// reflection.
func Unmarshal(b []byte, t reflect.Type) (pkt interface{}, err error) {
buf := bytes.NewBuffer(b)
p := reflect.New(t)
v := reflect.ValueOf(p)
for i := 0; i < t.NumField(); i++ {
f := v.Field(i)
switch f.Kind() {
case reflect.String:
// length of string
var l int16
var e error
e = binary.Read(buf, binary.BigEndian, &l)
if e != nil {
err = e
return
}
// read length-of-string bytes from the buffer
raw := make([]byte, l)
_, e = buf.Read(raw)
if e != nil {
err = e
return
}
// convert the bytes to a string
f.SetString(bytes.NewBuffer(raw).String())
default:
e := binary.Read(buf, binary.BigEndian, f.Addr())
if e != nil {
err = e
return
}
}
}
pkt = p
return
}
The problem with the code above is that the call to f.Addr() near the end is apparently trying to get the address of an unaddressable value.
If there is an alternative solution, I would appreciate that as well. Either way, any help would be much appreciated.
Thanks!
I think you should use
v := p.Elem() // Get the value that 'p' points to
instead of
v := reflect.ValueOf(p)
Working example with lots of assumptions and a trivial data format:
package main
import (
"fmt"
"reflect"
"strconv"
)
// example marshalled format. lets say that marshalled data will have
// four bytes of a formatted floating point number followed by two more
// printable bytes.
type m42 []byte
// example struct we'd like to unmarshal into.
type packet struct {
S string // exported fields required for reflection
F float64
}
// example usage
func main() {
var p packet
if err := Unmarshal(m42("3.14Pi"), &p); err == nil {
fmt.Println(p)
} else {
fmt.Println(err)
}
}
func Unmarshal(data m42, structPtr interface{}) error {
vp := reflect.ValueOf(structPtr)
ve := vp.Elem() // settable struct Value
vt := ve.Type() // type info for struct
nStructFields := ve.NumField()
for i := 0; i < nStructFields; i++ {
fv := ve.Field(i) // settable field Value
sf := vt.Field(i) // StructField type information
// struct field name indicates which m42 field to unmarshal.
switch sf.Name {
case "S":
fv.SetString(string(data[4:6]))
case "F":
s := string(data[0:4])
if n, err := strconv.ParseFloat(s, 64); err == nil {
fv.SetFloat(n)
} else {
return err
}
}
}
return nil
}
Appropriate alternative solutions would depend heavily on the real data you need to support.
I'm going to bet that the reason f.Addr() has the problem because it actually isn't addressable.
the reflect package Type object has a method that will tell you if the type is addressable called CanAddr(). Assuming the field is addressable if it's not a string is not always true. If the struct is not passed in as a pointer to a struct then it's fields won't be addressable. For more details about what is and isn't addressable see: http://weekly.golang.org/pkg/reflect/#Value.CanAddr which outlines the correct rules.
Essentially for your code to work I think you need to ensure you always call it with a pointer to a struct.

Resources