Go json array of arrays - arrays

I've troubles with parsing json array of arrays using golang, all of them without names:
[[1594561500000, 1031.47571376], [1594562500000, 1031.43571376],[1595561500000, 1041.41376]]
Could you help me with it?

Don't forget to convert the string you use to hold the JSON to []byte first:
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
s := []byte(`[[1594561500000, 1031.47571376], [1594562500000, 1031.43571376],[1595561500000, 1041.41376]]`)
var nums [][]float64
if err := json.Unmarshal(s, &nums); err != nil {
log.Fatal(err)
}
fmt.Println(nums)
}
Try it on the Go Playground.

Related

In Go, how to Unmarshal bson byte[] data into an array of structs?

What's the best way to Unmarshal bson byte[] data into an array of structs, when the array of structs is passed into an interface{} parameter?
For demonstration purposes, in the following code, I use bson.Marshal() on the inStructArr to get the byte[] type of data. This is so I can use bson.Unmarshal(...) to pipe into the outStructArr.
import "gopkg.in/mgo.v2/bson"
type User struct {
Name string
}
func DecodeArrData(inStructArr, outStructArr interface{}) {
inStructArrData, _ := bson.Marshal(inStructArr)
bson.Unmarshal(inStructArrData, outStructArr) // <-- Error happens here
// What's the right way of accomplishing this?
}
func Main() {
outUsers := &[]User{}
inUsers := []User{User{"A"}, User{"B"}}
DecodeArrData(inUsers, outUsers)
}
When I do this, the error-message I get is: Unsupported document type for unmarshalling: []User. What's the right way of doing this?
Thanks in advance!
The Marshal and Unmarshal functions work with BSON documents, not BSON arrays.
Wrap the slices in a struct to provide the document expected by the package:
func DecodeArrData(inStructArr, outStructArr interface{}) error {
in := struct{ Data interface{} }{Data: inStructArr}
inStructArrData, err := bson.Marshal(in)
if err != nil {
return err
}
var out struct{ Data bson.Raw }
if err := bson.Unmarshal(inStructArrData, &out); err != nil {
return err
}
return out.Data.Unmarshal(outStructArr)
}
If you are willing to take advantage of an undocumented feature of the Marshal function and add some BSON format knowledge to your application, then you can omit the wrapper.
The undocumented feature of Marshal is that it encodes slices as BSON arrays. The BSON array can be decoded using a bson.Raw value with Kind set the the BSON code for arrays (the value 4) and Data set to the array data:
func DecodeArrData(inStructArr, outStructArr interface{}) error {
inStructArrData, err := bson.Marshal(inStructArr)
if err != nil {
return err
}
raw := bson.Raw{Kind: 4, Data: inStructArrData}
return raw.Unmarshal(outStructArr)
}

How to parse JSON array in Go

How to parse a string (which is an array) in Go using json package?
type JsonType struct{
Array []string
}
func main(){
dataJson = `["1","2","3"]`
arr := JsonType{}
unmarshaled := json.Unmarshal([]byte(dataJson), &arr.Array)
log.Printf("Unmarshaled: %v", unmarshaled)
}
The return value of Unmarshal is an error, and this is what you are printing out:
// Return value type of Unmarshal is error.
err := json.Unmarshal([]byte(dataJson), &arr)
You can get rid of the JsonType as well and just use a slice:
package main
import (
"encoding/json"
"log"
)
func main() {
dataJson := `["1","2","3"]`
var arr []string
_ = json.Unmarshal([]byte(dataJson), &arr)
log.Printf("Unmarshaled: %v", arr)
}
// prints out:
// 2009/11/10 23:00:00 Unmarshaled: [1 2 3]
Code on play: https://play.golang.org/p/GNWlylavam
Background: Passing in a pointer allows Unmarshal to reduce (or get entirely rid of) memory allocations. Also, in a processing context, the caller may reuse the same value to repeatedly - saving allocations as well.
Note: This answer was written before the question was edited. In the original question &arr was passed to json.Unmarshal():
unmarshaled := json.Unmarshal([]byte(dataJson), &arr)
You pass the address of arr to json.Unmarshal() to unmarshal a JSON array, but arr is not an array (or slice), it is a struct value.
Arrays can be unmarshaled into Go arrays or slices. So pass arr.Array:
dataJson := `["1","2","3"]`
arr := JsonType{}
err := json.Unmarshal([]byte(dataJson), &arr.Array)
log.Printf("Unmarshaled: %v, error: %v", arr.Array, err)
Output (try it on the Go Playground):
2009/11/10 23:00:00 Unmarshaled: [1 2 3], error: <nil>
Of course you don't even need the JsonType wrapper, just use a simple []string slice:
dataJson := `["1","2","3"]`
var s []string
err := json.Unmarshal([]byte(dataJson), &s)

GO:array/slice to regular string

I am sort of new to golang, and also kind of new to programming. And go has been very hard for me. This is one thing that always confuses me: data types. If you run this(not on the playground) then it will spit out:
./main.go:40: cannot use recorded (type string) as type SVC in append
and if I reverse the values in the append call, it will spit out:
./main.go:40: first argument to append must be slice; have string
What I am trying to do is grab all of the stuff in the home directory, append all of the values with the modifications to an array, then put the array into a file using ioutil. All I want(as of now) is to append the values to the slice in func record. Can anybody help?
package main
import "os"
import "io/ioutil"
import "fmt"
type SVC struct {
key string
val string
}
func main() {
os.Chdir("../../../../../../..")
var data, err = ioutil.ReadDir("home")
checkerr(err)
for _, data := range data {
fmt.Println(data.Name())
}
os.Chdir("home/beanboybunny/repos/bux/go/src/bux")
}
func checkerr(err1 error) {
if err1 != nil {
fmt.Println("error")
}
}
func writer(dataname string) {
f := "var "
uname := dataname
q := " = VVC{\n"
w := " bux: 1,\n"
e := " op: true,\n"
c := "}"
b2 := f + uname + q + w + e + c
record(b2)
}
func record(recorded string) {
cache := []SVC{}
record SVC := recorded
appenda := append(cache, recorded)
}
Your type SVC struct has two private string fields. If you just want an array of strings you don't need the SVC type.
If you're just trying to build an array of strings with your specially formatted transform of the items in the home directory, here is a start showing the use of append and format strings: https://play.golang.org/p/eUKTKRxwfp
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
func main() {
os.Chdir("../../../../../../..")
var data, err = ioutil.ReadDir("home")
checkerr(err)
lines := []string{}
for _, data := range data {
fmt.Println(data.Name())
lines = append(lines, buildLine(data.Name()))
}
fmt.Println(strings.Join(lines, "\n"))
os.Chdir("home/beanboybunny/repos/bux/go/src/bux")
}
func checkerr(err1 error) {
if err1 != nil {
fmt.Printf("error: %v", err1)
}
}
func buildLine(dataname string) string {
return fmt.Sprintf("var %s = VVC{\n bux: 1,\n op: true,\n}", dataname)
}
You can not add meters to liters. So you can't append string type to a list of SVC records.
If you want []SVC as output (list of SVC) you need to implement a parser that transform a string to an SVC object and append it.
If you want []string as output you need to implement a serializer that transform an SVC to a string and append it.
How to do it is a separate question that is out of scope for this question.

Fixed-size array that contains multiple specific types?

I have an array (coming from JSON) that always contains a string and an int, like so: ["foo",42]
Right now, I have to use []interface{} with assertions arr[0].(string) arr[1].(int)
I'm wondering if there's any way to specify the types expected in the array? I'm picturing something like.. [...]{string,int}
Thanks.
At the first, answer is No. But you can get values from interface{} with type you expected.
How about this?
package main
import (
"encoding/json"
"fmt"
"github.com/mattn/go-scan"
"log"
)
func main() {
text := `["foo", 42]`
var v interface{}
err := json.Unmarshal([]byte(text), &v)
if err != nil {
log.Fatal(err)
}
var key string
var val int
e1, e2 := scan.ScanTree(v, "[0]", &key), scan.ScanTree(v, "[1]", &val)
if e1 != nil || e2 != nil {
log.Fatal(e1, e2)
}
fmt.Println(key, val)
}

Unmarshalling top-level JSON array in Go

I'm learning Go by writing a simple http server and I need to handle some JSON responses.
With an object response, I can unmarshal it idiomatically with 2 lines of code:
structResult := Foo{}
json.Unmarshal(structBody, &structResult)
I don't know how to do the same for an array response (see the example below). Is there a way to specify (possibly via json tag) that top-level array should go into a given struct field?
package main
import "fmt"
import "encoding/json"
type Foo struct {
Id uint64 `json:"id"`
Name string `json:"name"`
}
type BaseResult struct {
Error string `json:"error"`
}
type FooResult struct {
BaseResult
Foos []Foo
}
func main() {
// Simple and works.
structBody := []byte(`{"id": 1,"name": "foo"}`)
structResult := Foo{}
json.Unmarshal(structBody, &structResult)
fmt.Printf("%#v\n", structResult)
// Doesn't work.
arrayBody := []byte(`[{"id": 1,"name": "foo"},{"id": 2,"name": "bar"},{"id": 3,"name": "foobar"}]`)
arrayResult := FooResult{}
json.Unmarshal(arrayBody, &arrayResult)
fmt.Printf("%#v\n", arrayResult)
}
I know I could make FooResult an array:
type FooResult []Foo
but then I lose the ability to specify base object which I would like to use to store error message and such. I also know that I can unmarshal into &fooResult.Foos directly, but I want the code to work with both objects and arrays.
UPDATE
Implementing UnmarshalJSON as suggested by #dyoo partially solves my problem, but I was hoping that I could use BaseResult to store parse error in case JSON has a different structure:
arrayBody := []byte(`{"error": "foo"}`)
arrayResult := FooResult{}
json.Unmarshal(arrayBody, &arrayResult)
fmt.Printf("%#v\n", arrayResult)
Of course I could implement more complex logic inside UnmarshalJSON - but isn't there a simpler way to do it?
You can implement the json.Unmarshaler interface in your FooResult, to customize exactly how it responds to unmarshaling. (Similarly, there's a json.Marshaler interface.)
Add:
func (f *FooResult) UnmarshalJSON(bs []byte) error {
return json.Unmarshal(bs, &f.Foos)
}
after which your code should otherwise work. http://play.golang.org/p/oMdoB2e-rB
You might try something like:
func (f *FooResult) UnmarshalJSON(bs []byte) error {
err1 := json.Unmarshal(bs, &f.BaseResult)
err2 := json.Unmarshal(bs, &f.Foos)
if err1 != nil && err2 != nil {
// Arbitrarily choose an error.
return err1
}
return nil
}
although even this is beginning to look dubious. Handling union type results is not quite what the json library is designed to handle automatically for you. You'll need to explicitly code the coercion logic if your JSON has dynamic type.
See: How to unmarshall an array of different types correctly? and http://blog.golang.org/json-and-go for related issues.
Just specify Foos when you Unmarshal
package main
import "fmt"
import "encoding/json"
type Foo struct {
Id uint64 `json:"id"`
Name string `json:"name"`
}
type BaseResult struct {
Error string `json:"error"`
}
type FooResult struct {
BaseResult
Foos []Foo
}
func main() {
// Simple and works.
structBody := []byte(`{"id": 1,"name": "foo"}`)
structResult := Foo{}
json.Unmarshal(structBody, &structResult)
fmt.Printf("%#v\n", structResult)
// Doesn't work.
arrayBody := []byte(`[{"id": 1,"name": "foo"},{"id": 2,"name": "bar"},{"id": 3,"name": "foobar"}]`)
arrayResult := FooResult{}
if err := json.Unmarshal(arrayBody, &arrayResult.Foos); err != nil {
arrayResult.BaseResult.Error = string(arrayBody)
}
fmt.Printf("%#v\n", arrayResult)
}

Resources