Having trouble decoding JSON response [duplicate] - json

This question already has answers here:
My structures are not marshalling into json [duplicate]
(3 answers)
Closed 7 years ago.
This is my first attempt at Go and I feel I'm missing something important here. Trying to decode a JSON message from a webservice but the output I'm getting is:
{response:{requests:[]}}
All I'm really interested in is the data within the request node. My for-loop obviously isn't getting called because the array is empty. I feel like my structs need to be declared exactly as they appear in the webservice?
Sample JSON:
{
"response": {
"requests": [
{
"request": {}
},
{
"request": {
"id": 589748,
"image_thumbnail": "",
"description": "Blah blah",
"status": "received",
"user": "test"
}
}
],
"count": "50",
"benchmark": 0.95516896247864,
"status": {},
"debug": {}
}
}
type Request struct {
id int `json:"id"`
description string `json:"description"`
user string `json:"user"`
}
type Requests struct {
request Request `json:"request"`
}
type Response struct {
requests []Requests `json:"requests"`
}
type RootObject struct {
response Response `json:"response"`
}
url := "<webservice>"
req, err := http.NewRequest("GET", url, nil)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var r RootObject
decoder := json.NewDecoder(resp.Body)
decoder.Decode(&r)
fmt.Printf("%+v", r)
for _, req := range r.response.requests {
fmt.Printf("%d = %s\n", req.request.id, req.request.user)
}

Field names need to begin with upper case character to be exported identifiers.

Related

`json.Marshal()` converts an array field of a struct to map which it shouldn't

I want to serialize object of types.Project from github.com/compose-spec into JSON
However, a field of ths struct which is supposed to be array, is always serialized into map.
package main
import (
"encoding/json"
types "github.com/compose-spec/compose-go/types"
)
func main() {
out := types.Project{
Name: "foo",
Services: []types.ServiceConfig{ // <- notice this field is an array
{
Name: "bar",
Image: "hello-world",
},
},
}
buf, err := json.Marshal(out)
if err != nil {
panic(err)
}
println(string(buf)) // <- notice the Services field now a map, which is incorrect!
var in types.Project
if err := json.Unmarshal(buf, &in); err != nil {
panic(err)
}
}
The code fails to run:
{"name":"foo","services":{"bar":{"command":null,"entrypoint":null,"image":"hello-world"}}}
panic: json: cannot unmarshal object into Go struct field Project.services of type types.Services
goroutine 1 [running]:
main.main()
/tmp/sandbox1081064727/prog.go:29 +0x168
The out object is serialized as
{
"name": "foo",
"services": {
"bar": {
"command": null,
"entrypoint": null,
"image": "hello-world"
}
}
}
which should really be something like
{
"name": "foo",
"services": [
{
"name": "bar",
"command": null,
"entrypoint": null,
"image": "hello-world"
}
]
}
The behavior you're seeing is correct with respect to the compose file specification, which says:
A Compose file MUST declare a services root element as a map whose keys are string representations of service names, and whose values are service definitions.
The transformation of the list to a map is implemented by the custom marshalers in compose-go/types.go:
// MarshalYAML makes Services implement yaml.Marshaller
func (s Services) MarshalYAML() (interface{}, error) {
services := map[string]ServiceConfig{}
for _, service := range s {
services[service.Name] = service
}
return services, nil
}
// MarshalJSON makes Services implement json.Marshaler
func (s Services) MarshalJSON() ([]byte, error) {
data, err := s.MarshalYAML()
if err != nil {
return nil, err
}
return json.MarshalIndent(data, "", " ")
}

Golang unmarshaling JSON to protobuf generated structs

I would like to reqeive a JSON response withing a client application and unmarshal this response into a struct. To ensure that the struct stays the same accross all client apps using this package, I would like to define the JSON responses as protobuf messages. I am having difficulties unmarshaling the JSON to the protobuf generated structs.
I have the following JSON data:
[
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]
I have modeled my protobuf definitions like this:
syntax = "proto3";
package main;
message Container {
string name = 1;
string type = 2;
}
message Containers {
repeated Container containers = 1;
}
Using this pattern with structs normaly works, but for some reason using these proto definitions causes issues. The below code demonstrates a working and a non-working example. Although one of the versions work, I am unable to use this solution, since []*Container does not satisfy the proto.Message interface.
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/gogo/protobuf/jsonpb"
)
func working(data string) ([]*Container, error) {
var cs []*Container
return cs, json.Unmarshal([]byte(data), &cs)
}
func notWorking(data string) (*Containers, error) {
c := &Containers{}
jsm := jsonpb.Unmarshaler{}
if err := jsm.Unmarshal(strings.NewReader(data), c); err != nil {
return nil, err
}
return c, nil
}
func main() {
data := `
[
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]`
w, err := working(data)
if err != nil {
panic(err)
}
fmt.Print(w)
nw, err := notWorking(data)
if err != nil {
panic(err)
}
fmt.Print(nw.Containers)
}
Running this gives the following output:
[name:"C1" type:"docker" name:"C2" type:"docker" ]
panic: json: cannot unmarshal array into Go value of type map[string]json.RawMessage
goroutine 1 [running]:
main.main()
/Users/example/go/src/github.com/example/example/main.go:46 +0x1ee
Process finished with exit code 2
Is there a way to unmarshal this JSON to Containers? Or alternatively, make []*Container to satisfy the proto.Message interface?
For the message Containers, i.e.
message Containers {
repeated Container containers = 1;
}
The correct JSON should look like:
{
"containers" : [
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]
}
If you cannot change the JSON then you can utilize the func that you've created
func working(data string) ([]*Container, error) {
var cs []*Container
err := json.Unmarshal([]byte(data), &cs)
// handle the error here
return &Containers{
containers: cs,
}, nil
}
You should use NewDecoder to transfer the data to jsonDecoder and then traverse
the array.The code is this
func main() {
data := `
[
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]`
jsonDecoder := json.NewDecoder(strings.NewReader(data))
_, err := jsonDecoder.Token()
if err != nil {
log.Fatal(err)
}
var protoMessages []*pb.Container
for jsonDecoder.More() {
protoMessage := pb.Container{}
err := jsonpb.UnmarshalNext(jsonDecoder, &protoMessage)
if err != nil {
log.Fatal(err)
}
protoMessages = append(protoMessages, &protoMessage)
}
fmt.Println("%s", protoMessages)
}

Parse json body array sent in POST request and print

I am having one issue while reading the json array. Need help for below query.
Request Json :
{ "httpReq": {
"username": "1234567890",
"password": "1234567890",
"number": "123456"
}
}
Response Json :
{ "httpResp": {
"status": "Pass",
"message": "great"
}
}
Below was my code: If i am passing the json object below its working, but i need to send "httpReq" in json.
package main
import (
"encoding/json"
"fmt"
)
type people struct {
Username string `json:"username"`
Password string `json:"password"`
Number string `json:"number"`
}
type peopleread struct {
Collection []people
}
func main() {
text := `{
"username": "1234567890",
"password": "1234567890",
"number": "123456"
}`
textBytes := []byte(text)
//people1 := people{}
var people2 people
err := json.Unmarshal(textBytes, &people2)
if err != nil {
fmt.Println(err)
return
}
Username := people2.Username
Password := people2.Password
Number := people2.Number
fmt.Println(Username)
fmt.Println(Password)
fmt.Println(Number)
}
To unmarshal with httpReq field you have to handle this.
Create a struct to your wrap your request body like json
type HttpReq struct{
HttpReq people `json:"httpReq"`
}
Then use unmarshal
var httpReq HttpReq
err := json.Unmarshal(textBytes, &httpReq)
Full code in Go playground here

Go json.Unmarshall() works with single entity but not with slice

I'm using Go for a simple http client. Here's the entity I'm unmarshalling:
type Message struct {
Id int64
Timestamp int64
Text string
Author User
LastEdited int64
}
type User struct {
Id int64
Name string
}
A single entity looks like this in JSON:
{
"text": "hello, can you hear me?",
"timestamp": 1512964818565,
"author": {
"name": "andrea",
"id": 3
},
"lastEdited": null,
"id": 8
}
Go/json has no problem unmarshalling the single entity:
var m Message
err = json.Unmarshal(body, &m)
if err != nil {
printerr(err.Error())
}
println(m.Text)
However, if the return of the endpoint is multiple entities:
[
{
"text": "hello, can you hear me?",
"timestamp": 1512964800981,
"author": {
"name": "eleven",
"id": 4
},
"lastEdited": null,
"id": 7
}
]
And I change my corresponding Unmarshall to work on a slice of structs, Go throws an error:
var m []Message
err = json.Unmarshal(body, &m)
if err != nil {
printerr(err.Error()) // unexpected end of JSON input
}
for i := 0; i < len(m); i++ {
println(m[i].Text)
}
What gives?
Works fine for me (try it on playground), where are you getting the payload data from? sounds like that's truncating it.
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Id int64
Timestamp int64
Text string
Author User
LastEdited int64
}
type User struct {
Id int64
Name string
}
func main() {
body := []byte(`[
{
"text": "hello, can you hear me?",
"timestamp": 1512964800981,
"author": {
"name": "eleven",
"id": 4
},
"lastEdited": null,
"id": 7
}
]`)
var m []Message
err := json.Unmarshal(body, &m)
if err != nil {
fmt.Printf("error: %v") // unexpected end of JSON input
}
for i := 0; i < len(m); i++ {
fmt.Println(m[i].Text)
}
}
running it gives this output
hello, can you hear me?

Decode JSON with Go

I have a reaction struct like this
type Reaction struct {
Id uint `json:"id" form:"id"`
ReactionType uint `json:"reactionType" form:"reactionType"`
PostId uint `json:"postId" form:"postId"`
ReactorId uint `json:"reactorId" form:"reactorId"`
CreatedAt string `json:"createdAt" form:"createdAt"`
UpdatedAt string `json:"updatedAt" form:"createdAt"`
}
And I have a function that consumes an API that should return a slice of Reaction
var myClient = &http.Client{Timeout: 7 * time.Second}
func getJson(url string, result interface{}) error {
req, _ := http.NewRequest("GET", url, nil)
resp, err := myClient.Do(req)
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)
}
err = json.NewDecoder(resp.Body).Decode(result)
if err != nil {
return fmt.Errorf("cannot decode JSON: %v", err)
}
return nil
}
But somehow it failed to display the array/slice of objects that I want to retrieve, I got no data at all. Where did I miss?
func main(){
..
..
var reactList []Reaction
getJson("http://localhost:80/reactions", reactList)
for _, r := range reactList {
fmt.Print(r.ReactionType)
}
}
and this is the original responses
[
{
"id": 55,
"reactionType": 5,
"reactorId": 2,
"postId": 4,
"createdAt": "2017-11-18 14:23:29",
"updatedAt": ""
},
{
"id": 56,
"reactionType": 5,
"reactorId": 3,
"postId": 4,
"createdAt": "2017-11-18 14:23:42",
"updatedAt": ""
},
{
"id": 57,
"reactionType": 4,
"reactorId": 4,
"postId": 4,
"createdAt": "2017-11-18 14:23:56",
"updatedAt": ""
}
]
As pointed out in the comments you need to pass a pointer to getJson so that it can actually modify the contents of slice.
getJson("http://localhost:80/reactions", &reactList)
This is a close representation of what you have https://play.golang.org/p/_PnNK64giE, just see where things aren't in place in your case.
Check this playground: https://play.golang.org/p/VjocUtiDRN
Maybe this is what you want. So you should make your function getJson(btw getJSON is better name in Golang naming convention) takes []Reaction parameter, instead of interface{}. Then you can unmarshal your array-like response into slice of Reactions.
The main error here is that you're passing result without pointer. Should be:
getJson("http://localhost:80/reactions", &reactList)