Want to send empty json string "{}" instead of "null" - json

I'm trying to handle http requests in my go api, in which i wish to send empty json string like
{"result": {}, "status": "failed"}
but when the mysql query returns zero rows, it return an output as
{"result": null, "status": "failed"}
Edit : This is the response struct i'm using:
type Resp struct {
Result []map[string]interface{} `json:"result"`
Status string `json:"status"`
}
How can i handle this situation?

The Result field is a slice, which can be nil. This is rendered as null in JSON. To make it not nil, you will have to initialise it.
In addition, since Result is a slice, it will be marshalled to a JSON array ([]), not a JSON object ({}).
Example:
package main
import (
"encoding/json"
"fmt"
"log"
"os"
)
type Resp struct {
Result []map[string]interface{} `json:"result"`
Status string `json:"status"`
}
func main() {
enc := json.NewEncoder(os.Stdout)
fmt.Println("Empty Resp struct:")
if err := enc.Encode(Resp{}); err != nil {
log.Fatal(err)
}
fmt.Println()
fmt.Println("Initialised Result field:")
if err := enc.Encode(Resp{Result: []map[string]interface{}{}}); err != nil {
log.Fatal(err)
}
}
Output:
Empty Resp struct:
{"result":null,"status":""}
Initialised Result field:
{"result":[],"status":""}
https://play.golang.org/p/9zmfH-180Zk

Related

How to write unit test failure for json.NewDecoder.Decode?

I have to write unit tests for a function and this function uses json.NewDecoder.Decode
var infos models.RegisterInfos // struct with json fields
err := json.NewDecoder(r.Body).Decode(&infos)
if err != nil {
// do something
}
How can I simulate an error in a unit test (using the testing package) for json.NewDecoder(r.Body).Decode(&infos) ? I tried looking in the NewDecoder and Decode source code but I couldn't find anything that can generate an error in just a few lines.
you could send a body like <invalid json> as example:
func main() {
body := "<invalid json>"
var infos RegisterInfos // struct with json fields
err := json.NewDecoder(strings.NewReader(body)).Decode(&infos)
if err != nil {
fmt.Println(err)
}
}
See https://go.dev/play/p/44E99D0eQou
Feed it an invalid input, or decode into an invalid output:
package main
import (
"encoding/json"
"fmt"
"strings"
"testing"
)
type Message struct {
Name string
}
func TestDecodeFail(t *testing.T) {
for _, tc := range []struct {
in string
desc string
out any
}{
{`{Name: "Bobby"}`, "key without quotes", &Message{}},
{`{"Name": "Foo"a}`, "extra character", &Message{}},
{`{"Name": "Foo"}`, "bad destination", &struct{ Name int64 }{}},
{`{"Name": "Foo` + "\u001a" + `"}`, "invalid character", &Message{}},
{`{"Name": "Foo"}`, "unmarshal to nil", (*Message)(nil)},
} {
err := decode(tc.in, tc.out)
if err != nil {
fmt.Printf("%s -> %s, %T\n", tc.desc, err.Error(), err)
}
}
}
func decode(in string, out any) error {
return json.NewDecoder(strings.NewReader(in)).Decode(out)
}
Outputs:
key without quotes -> invalid character 'N' looking for beginning of object key string, *json.SyntaxError
extra character -> invalid character 'a' after object key:value pair, *json.SyntaxError
bad destination -> json: cannot unmarshal string into Go struct field .Name of type int64, *json.UnmarshalTypeError
invalid character -> invalid character '\x1a' in string literal, *json.SyntaxError
unmarshal to nil -> json: Unmarshal(nil *main.Message), *json.InvalidUnmarshalError

Add a field to JSON ( struct + interface ) golang [duplicate]

This question already has answers here:
Adding Arbitrary fields to json output of an unknown struct
(2 answers)
Closed 1 year ago.
Here's the response interface :
type Response interface{}
It's satisfied by a struct like this :
type CheckResponse struct {
Status string `json:"status"`
}
I am getting out []Response as an output which is to be consumed elsewhere.
I want to add a Version string to this JSON, before it's being sent. I've tried using anonymous structs ( but in vain ) :
for _, d := range out {
outd := struct {
Resp Response `json:",inline"`
Version string `json:",inline"`
}{
Resp: d,
Version: "1.1",
}
data, _ := json.Marshal(outd)
log.Infof("response : %s", data)
}
The output I am getting is :
response : {"Resp":{"status":"UP"},"Version":"1.1"}
What I want is
{"status":"UP","Version":"1.1"}
i.e. one single flat JSON.
Assert your d to CheckResponse type and then define dynamic struct like this
outd := struct {
Resp string `json:"status,inline"`
Version string `json:",inline"`
}
This is the full code for this.
package main
import (
"encoding/json"
"fmt"
)
type Response interface {}
type CheckResponse struct {
Status string `json:"status"`
}
func main() {
out := []Response{
CheckResponse{Status: "UP"},
}
for _, d := range out {
res, ok := d.(CheckResponse)
if !ok {
continue
}
outd := struct {
Resp string `json:"status,inline"`
Version string `json:",inline"`
}{
Resp: res.Status,
Version: "1.1",
}
data, _ := json.Marshal(outd)
fmt.Printf("response : %s", data)
}
}
You can run here
inline tag is not supported by encoding/json and embedding interfaces will also not produce the result you want. You'll have to declare a type for the out value and have that type implement the json.Marshaler interface, you can then customize how its fields are marshaled, for example you could marshal the two fields Resp and Version separately and then "merge the result" into a single json object.
type VersionedResponse struct {
Resp Response
Version string
}
func (r VersionedResponse) MarshalJSON() ([]byte, error) {
out1, err := json.Marshal(r.Resp)
if err != nil {
return nil, err
}
out2, err := json.Marshal(struct{ Version string }{r.Version})
if err != nil {
return nil, err
}
// NOTE: if Resp can hold something that after marshaling
// produces something other than a json object, you'll have
// to be more careful about how you gonna merge the two outputs.
//
// For example if Resp is nil then out1 will be []byte(`null`)
// If Resp is a slice then out1 will be []byte(`[ ... ]`)
out1[len(out1)-1] = ',' // replace '}' with ','
out2 = out2[1:] // remove leading '{'
return append(out1, out2...), nil
}
https://play.golang.org/p/66jIYXGUtWJ
One way that will work for sure is simply use a map[string]interface{}, iterate over fields in Response via reflect or use a library like structs, update your map with response fields, append your version field to map, and then marshal.
Here is an example
package main
import (
"encoding/json"
"fmt"
"github.com/fatih/structs"
)
type Response interface{}
type CheckResponse struct {
Status string `json:"status"`
}
func main() {
resp := CheckResponse{Status: "success"}
m := structs.Map(resp)
m["Version"] = "0.1"
out, _ := json.Marshal(m)
fmt.Println(string(out))
}

Parse Error from Converting Json String to Struct

i cannot parse the json value i am sending a playground link
Any idea about that? here is the link and codes
https://play.golang.org/p/qhZpS_-618s
package main
import (
"encoding/json"
"fmt"
//mapstructure "github.com/mitchellh/mapstructure"
)
type presence struct{
id string
m_type string
deny string
}
type jsonHandler struct {
name string
dat map[string]interface{}
}
func main() {
s := `["Presence",{"id":"905356870666#c.us","type":"unavailable","deny":true}]`
data := jsonHandler{}
json.Unmarshal([]byte(s), &data)
fmt.Printf("Operation: %s", data.name)
}
Output :
Operation:
Program exited.
Try with this one: https://play.golang.com/p/UICf_uNNFdC
I've commented a lot in order to enhance code readability. Be sure to handle error properly and remove debug print.
package main
import (
"encoding/json"
"log"
"strings"
)
type Presence struct {
Presence string
ID string `json:"id"`
Type string `json:"type"`
Deny bool `json:"deny"`
}
type JsonHandler struct {
Name string `json:"name"`
Dat Presence `json:"dat"`
}
func main() {
var (
// Used for unmarshal a given json
packedData []json.RawMessage
err error
// Data that does not have a related json key
name []byte
// Used for extract the raw data that will be unmarshalled into the Presence struct
temp []byte
// Nested json
jsonPresence Presence
handler JsonHandler
)
s := `["Presence",{"id":"905356870666#c.us","type":"unavailable","deny":true}]`
log.Println("Dealing with -> " + s)
// Unmarshall into a raw json message
err = json.Unmarshal([]byte(s), &packedData)
if err != nil {
panic(err)
}
// Extract the presence
log.Println("Presence: ", string(packedData[0]))
// Extract the nested json
log.Println("Packed: ", string(packedData[1]))
// NOTE: 0 refers to the first value of the JSON
name, err = packedData[0].MarshalJSON()
if err != nil {
panic(err)
}
log.Println("Value that does not have a key: " + string(name))
handler.Name = strings.Replace(string(name), "\"", "", -1)
// NOTE: 1 refers to the second value of the JSON, the entire JSON
// Unmarshal the nested Json into byte
temp, err = packedData[1].MarshalJSON()
if err != nil {
panic(err)
}
// Unmarshal the raw byte into the struct
err = json.Unmarshal(temp, &jsonPresence)
if err != nil {
panic(err)
}
log.Println("ID:", jsonPresence.ID)
log.Println("Type:", jsonPresence.Type)
log.Println("Deny:", jsonPresence.Deny)
handler.Dat = jsonPresence
log.Println("Data unmarshalled: ", handler)
}
Go Playground Link: https://play.golang.org/p/qe0jyFVNTH1
Few Problem are present in this:
1. Json Package can't refer the Unexported Structure Elements.So please use Deny instead of deny in the following snippet.This is applicable to all variables declared inside the structure
2. The json fields tag are incorrect. eg.mapstructure:"id" should be json:"id"
3. The json to be parsed contains two distinct elements i.e string "Presence" and nested json object.It can't be parsed as a single element.It is better to declare "Presence" as a key and nested json as the value.
4. The deny variable should be bool rather than string
Wow,solved problem by adding only these codes
Here Go Lang Link : https://play.golang.org/p/doHNWK58Cae
func (n *JsonHandler) UnmarshalJSON(buf []byte) error {
tmp := []interface{}{&n.Name, &n.Dat}
wantLen := len(tmp)
if err := json.Unmarshal(buf, &tmp); err != nil {
return err
}
if g, e := len(tmp), wantLen; g != e {
return fmt.Errorf("wrong number of fields in Notification: %d != %d", g, e)
}
return nil
}

How to unmarshal json data to print in a well defined format

I can't figure out how to unmarshal the json data provided by an api and consume the data to print in a specified format.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type postOffice []struct {
Name string
Taluk string
Region string
Country string
}
func main() {
data, err := http.Get("http://postalpincode.in/api/pincode/221010")
if err != nil {
fmt.Printf("The http request has a error : %s", err)
} else {
read, _ := ioutil.ReadAll(data.Body)
var po postOffice
err = json.Unmarshal(read, &po)
if err != nil {
fmt.Printf("%s", err)
}
fmt.Print(po)
}
}
The code was working well till the "read" was evaluated but is throwing the following error on using json.Unmarshal "json: cannot unmarshal object into Go value of type main.post[]"
You need create a second struct to receive the whole JSON.
type JSONResponse struct {
Message string `json:"Message"`
Status string `json:"Success"`
PostOffice postOffice `json:"PostOffice"`
}
This is because the PostOffice is an array inside of the response.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
//this is the new struct
type JSONResponse struct {
Message string `json:"Message"`
Status string `json:"Success"`
PostOffice postOffice `json:"PostOffice"`
}
type postOffice []struct {
Name string
Taluk string
Region string
Country string
}
func main() {
data, err := http.Get("http://postalpincode.in/api/pincode/221010")
if err != nil {
fmt.Printf("The http request has a error : %s", err)
} else {
read, _ := ioutil.ReadAll(data.Body)
//change the type of the struct
var po JSONResponse
err = json.Unmarshal(read, &po)
if err != nil {
fmt.Printf("%s", err)
}
fmt.Print(po)
}
}

How to parse JSON string to struct

I have struct of Request, value is optional:
type Request struct {
Operation string `json:"operation"`
Key string `json:"key"`
Value string `json:"value"`
}
And function that should parse json string to struct^
go func() {
s := string("{'operation': 'get', 'key': 'example'}")
data := Request{}
json.Unmarshal([]byte(s), data)
log.Printf("Operation: %s", data.Operation)
}
For some reason data.Operation is empty. What is wrong here?
Two problems, first, your json is invalid, it needs to use " instead of '
Second, you have to unmarshal into &data and not to data
https://play.golang.org/p/zdMq5_ex8G
package main
import (
"fmt"
"encoding/json"
)
type Request struct {
Operation string `json:"operation"`
Key string `json:"key"`
Value string `json:"value"`
}
func main() {
s := string(`{"operation": "get", "key": "example"}`)
data := Request{}
json.Unmarshal([]byte(s), &data)
fmt.Printf("Operation: %s", data.Operation)
}
Side note, you would have seen this, if you would have been checking your errors:
s := string("{'operation': 'get', 'key': 'example'}")
err := json.Unmarshal([]byte(s), data)
if err != nil {
fmt.Println(err.Error())
//json: Unmarshal(non-pointer main.Request)
}
err := json.Unmarshal([]byte(s), &data)
if err != nil {
fmt.Println(err.Error())
//invalid character '\'' looking for beginning of object key string
}