Using Golang json.NewDecoder / json.NewEncoder - json

I'm a total noob at go and I'm trying to understand what I'm missing here. I'm expecting to use dec.Decode to loop over the json values and end up with a map of the response. What I'm getting is the entire json string as the key to the first element of the map. What am I missing?
Example response:
2015/03/02 10:03:16 map[error:invalid_request error_description:that is not a recognized WePay API call error_code:1001] map[string]interface {}
package main
import (
"encoding/json"
"io"
"log"
"net/http"
"reflect"
)
func main() {
var v map[string]interface{}
resp, err := http.Get("https://wepayapi.com/v2/")
if err != nil {
log.Println("Error: " + err.Error())
}
defer resp.Body.Close()
// resp.Body is an io.ReadCloser... NewDecoder expects an io.Reader
dec := json.NewDecoder(resp.Body)
// Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
for err := dec.Decode(&v); err != nil && err != io.EOF; {
log.Println("ERROR: " + err.Error())
return
}
log.Println(v, reflect.TypeOf(v))
}

Decoder will decode the whole JSON value at once (in this case the error object), you don;y have to call it in a loop:
if err := dec.Decode(&v); err != nil {
log.Println("ERROR: " + err.Error())
return
}
As a response you get a map equavalent of this JSON:
{"error":"invalid_request","error_description":"that is not a recognized WePay API call","error_code":1001}
Result:
map[string]interface{} {
"error":"invalid_request",
"error_description":"that is not a recognized WePay API call",
"error_code":1001,
}

Related

json: cannot unmarshal string into Go value of type MyMap.map

I met problem with marshaling JSON in golang.
I need to unmarshal json object, that I recieve from UDP packets.
But I met problem with unmarshaling - it doesn't want to unmarshal properly.
I get "Error with unmarshaling: json: cannot unmarshal string into Go value of type main.MyMap" error.
I tested in different ways, but feel stuck on this example - marshaland unmarshal in one line, and still get errors.
package main
import (
"encoding/json"
"fmt"
"log"
)
type MyMap struct {
Param map[string]string `json:"packet"`
}
func main() {
rawJson := []byte(`{
"packet":{
"hostname":"host1",
"pid":"123435",
"processname":"process",
"type":"partial"}
}`)
data, err := json.Marshal(rawJson)
if err != nil {
log.Println("Error with marchal JSON: " + err.Error())
}
var unmarshaled MyMap
err = json.Unmarshal(data, &unmarshaled)
if err != nil {
fmt.Printf("Error with unmarshaling: %v", err)
return
}
fmt.Printf("Read a message from %v %s \n", unmarshaled.Param["pid"], unmarshaled.Param["processname"])
}
If I'm trying to unmarshal JSON, that I received from UDP, the error says
invalid character 'i/x01' looking for beginning of value
I believe I get this kind of error because of my misunderstanding of marshal system.
I'd be appreciate if you help me
Thanks!
you should change rawjson to string type and change your order code same as:
package main
import (
"encoding/json"
"fmt"
)
type MyMap struct {
Param map[string]string `json:"packet"`
}
func main() {
rawJson := `{
"packet":{
"hostname":"host1",
"pid":"123435",
"processname":"process",
"type":"partial"}
}`
struct_instance := MyMap{}
if er := json.Unmarshal([]byte(rawJson), &struct_instance); er != nil {
fmt.Println(er)
}
fmt.Println(struct_instance)
json_as_byte, er := json.Marshal(struct_instance)
if er != nil {
fmt.Println(er)
}
fmt.Println(string(json_as_byte))
}
I did few changes in your code and it's worked very well.
You can run it here: https://go.dev/play/p/jvw9MsVFbHt
type mp map[string]string
type MyMap struct {
Param mp `json:"packet"`
}
func main() {
rawJson := []byte(`{
"packet":{
"hostname":"host1",
"pid":"123435",
"processname":"process",
"type":"partial"}
}`)
data, err := json.Marshal(rawJson) //Not Required
if err != nil {
fmt.Println("Error with marchal JSON: " + err.Error())
}
fmt.Println("data ", data)
var res MyMap
json.Unmarshal(rawJson, &res)
fmt.Printf("Read a message from %v %s \n", res.Param["pid"], res.Param["processname"])
}

Submit variable in payload of golang http.NewRequest

I'm learning Golang so please excuse what may seem to be a basic question. I have searched for a couple of hours for clues as to how I might achieve sending variable data in my JSON formatted API POST from my golang app, but not found a clue or solution yet. I know the answer will be my lack of syntax knowledge.
So the problem is with the 'lastcontact' field I'm trying to POST. I want to use my 'dt' variable that contains the current datetime.
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
"time"
)
func main() {
dt := time.Now()
url := "https://fakeapi.io/API/apiActions/update/"
method := "POST"
payload := strings.NewReader(`{
"name" : "Dumpty",
"saveconfig" : "true",
"lastcontact" : {dt}
}`)
client := &http.Client {
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("api-key", "gjhgjhgjhg")
req.Header.Add("api-secret", "jhgjhgjhg")
req.Header.Add("Content-Type", "application/json; charset=UTF-8")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
You can use a struct to store your response type if you know the fields beforehand. If you don't, you could use a map[string]interface{} to store arbitrary data.
Then using json.Marshal to convert it to a correctly formatted JSON response.
type myStruct struct {
Name string `json:"name"`
SaveConfig string `json:"saveconfig"`
LastContact time.Time `json:"lastcontact"`
}
dt := time.Now()
myData := myStruct{
Name: "Dumpty",
SaveConfig: "true",
LastContact: dt,
}
myBytes, err := json.Marshal(myData)
// bytes.NewBuffer returns *bytes.Buffer
// which implements the io.Reader interface
// that you need for your http.NewRequest call
payload := bytes.NewBuffer(myBytes)
Full playground example https://play.golang.org/p/Mc9UXT32Wi1

Golang json query from bitcoin api returns invalid character

Something tells me I'm not understanding json correctly. I'm trying to grab some data off http://api.bitcoincharts.com/v1/trades.csv?symbol=rockUSD, but my Unmarshal seems to not be able to read the json data. I'm a fresh beginner to golang (and json as well), and I'm wondering how I am able to skip that wrong character error I'm making.
My error:
invalid character ',' after top-level value
panic: invalid character ',' after top-level value
My code:
package main
import ("fmt"
"net/http"
"io/ioutil"
"encoding/json"
)
type Prices struct {
Data string
}
func main() {
url := "http://api.bitcoincharts.com/v1/trades.csv?symbol=rockUSD"
httpresp, err := http.Get(url)
if err != nil{
fmt.Println(err)
panic(err)
}
defer httpresp.Body.Close()
htmldata, err := ioutil.ReadAll(httpresp.Body)
if err != nil{
fmt.Println(err)
panic (err)
}
var jsonData []Prices
err = json.Unmarshal([]byte(htmldata), &jsonData)
if err != nil {
fmt.Println(err)
panic (err)
}
fmt.Println(jsonData)
}
That is NOT json data at all, you'd have to write a custom parser.
Example:
.........
data := readData(httpresp.Body)
........
func readData(r io.Reader) (out [][3]float64) {
br := bufio.NewScanner(r)
for br.Scan() {
parts := strings.Split(br.Text(), ",")
if len(parts) != 3 {
continue
}
var fparts [3]float64
for i, p := range parts {
// bad idea to ignore errors, but it's left as exercise for the reader.
fparts[i], _ = strconv.ParseFloat(p, 64)
}
out = append(out, fparts)
}
return
}
playground

Golang: Convert to JSON.GZ and write to file

Trying to accomplish the following output with my data:
Convert to JSON string and write to file: output.json (this part is working)
Gzip Compress the JSON string and write that to a json.gz file: output.json.gz (NOT WORKING)
The code runs fine and writes to both files. But the gzipped file gives this error when I try to unzip it: Data error in 'output.json'. File is broken
Here's the code:
package main
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io/ioutil"
)
type Generic struct {
Name string
Cool bool
Rank int
}
func main() {
generic := Generic{"Golang", true, 100}
fileJson, _ := json.Marshal(generic)
err := ioutil.WriteFile("output.json", fileJson, 0644)
if err != nil {
fmt.Printf("WriteFileJson ERROR: %+v", err)
}
var fileGZ bytes.Buffer
zipper := gzip.NewWriter(&fileGZ)
defer zipper.Close()
_, err = zipper.Write([]byte(string(fileJson)))
if err != nil {
fmt.Printf("zipper.Write ERROR: %+v", err)
}
err = ioutil.WriteFile("output.json.gz", []byte(fileGZ.String()), 0644)
if err != nil {
fmt.Printf("WriteFileGZ ERROR: %+v", err)
}
}
What did I miss?
You need to call zipper.Close() immediately after finishing writing
http://play.golang.org/p/xNeMg3aXxO
_, err = zipper.Write(fileJson)
if err != nil {
log.Fatalf("zipper.Write ERROR: %+v", err)
}
err := zipper.Close() // call it explicitly and check error
Calling defer zipper.Close() would trigger the call at the end of the main function. Until you call .Close() the data is being written to an intermediate buffer and not flushed to the actual file.

How to get JSON object by calling a url in Go Language?

I'm starting to learn Golang and I would like to know how to get a json response by calling an url, if you could give me an example it would be great in order to guide myself.
Here's a simple example to get you started. Instead of a map[string]interface{} you should consider making a struct to hold the result of your request.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
func main() {
resp, err := http.Get("http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo")
if err != nil {
log.Fatal(err)
}
var generic map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&generic)
if err != nil {
log.Fatal(err)
}
fmt.Println(generic)
}
I'd write a little helper function to do it:
// getJSON fetches the contents of the given URL
// and decodes it as JSON into the given result,
// which should be a pointer to the expected data.
func getJSON(url string, result interface{}) error {
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("cannot fetch URL %q: %v", url, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected http GET status: %s", resp.Status)
}
// We could check the resulting content type
// here if desired.
err := json.NewDecoder(resp.Body).Decode(result)
if err != nil {
return fmt.Errorf("cannot decode JSON: %v", err)
}
return nil
}
A full working example can be found here: http://play.golang.org/p/b1WJb7MbQV
Note that it is important to check the status code as well as the Get error, and the response body must be closed explicitly (see the documentation here: http://golang.org/pkg/net/http/#Get)