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

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

Related

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 this json string

I'm having some problem reading this type of json.
["Msg",{"cmd":"ack","id":"B81DA375B6C4AA49D262","ack":2,"from":"18094158994#c.us","to":"18099897215#c.us","t":1555446115}]
i try with many libraries.
type SEND struct {
Mgs string `json:"Msg"`
//SEND MSG
}
type MSG struct {
CMD string `json:"cmd"`
ID string `json:"id"`
ACK int `json:"ack"`
FROM string `json:"from"`
TO string `json:"to"`
T int64 `json:"t"`
}
func main() {
data := `["Msg",{"cmd":"ack","id":"B81DA375B6C4AA49D262","ack":2,"from":"18094158994#c.us","to":"18099897215#c.us","t":1555446115}] `
var dd SEND
err := json.Valid([]byte(data))
fmt.Println("Is valid XML?->", err)
json.Unmarshal([]byte(data), &dd)
fmt.Println("1", dd)
fmt.Println("2", dd.Mgs)
}
Al always a receive empty
and the json it's valid
Is valid XML?-> true
1 {}
2 EMPTY
In this case you have array with string and object in your json, so you have to use interface{} on golang side, must be something like:
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := `["Msg",{"cmd":"ack","id":"B81DA375B6C4AA49D262","ack":2,"from":"18094158994#c.us","to":"18099897215#c.us","t":1555446115}] `
var d []interface{}
err := json.Unmarshal([]byte(data), &d)
fmt.Printf("err: %v \n", err)
fmt.Printf("d: %#v \n", d[0])
fmt.Printf("d: %#v \n", d[1])
}
Result will look like:
err: <nil>
d: "Msg"
d: map[string]interface {}{"id":"B81DA375B6C4AA49D262", "ack":2, "from":"18094158994#c.us", "to":"18099897215#c.us", "t":1.555446115e+09, "cmd":"ack"}
So 1st element in slice d is string Msg,
and 2nd element in slice is map map[string]interface {}
and now you can do something else with this map.

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
}

Converting Go struct to JSON

I am trying to convert a Go struct to JSON using the json package but all I get is {}. I am certain it is something totally obvious but I don't see it.
package main
import (
"fmt"
"encoding/json"
)
type User struct {
name string
}
func main() {
user := &User{name:"Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Printf("Error: %s", err)
return;
}
fmt.Println(string(b))
}
Then when I try to run it I get this:
$ 6g test.go && 6l -o test test.6 && ./test
{}
You need to export the User.name field so that the json package can see it. Rename the name field to Name.
package main
import (
"fmt"
"encoding/json"
)
type User struct {
Name string
}
func main() {
user := &User{Name: "Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(b))
}
Output:
{"Name":"Frank"}
Related issue:
I was having trouble converting struct to JSON, sending it as response from Golang, then, later catch the same in JavaScript via Ajax.
Wasted a lot of time, so posting solution here.
In Go:
// web server
type Foo struct {
Number int `json:"number"`
Title string `json:"title"`
}
foo_marshalled, err := json.Marshal(Foo{Number: 1, Title: "test"})
fmt.Fprint(w, string(foo_marshalled)) // write response to ResponseWriter (w)
In JavaScript:
// web call & receive in "data", thru Ajax/ other
var Foo = JSON.parse(data);
console.log("number: " + Foo.number);
console.log("title: " + Foo.title);
This is an interesting question, it is very easy using the new go versions. You should do this:
package main
import (
"fmt"
"encoding/json"
)
type User struct {
Name string `json:"name"`
}
func main() {
user := &User{name:"Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Printf("Error: %s", err)
return;
}
fmt.Println(string(b))
}
Change this name to Name.
You can define your own custom MarshalJSON and UnmarshalJSON methods and intentionally control what should be included, ex:
package main
import (
"fmt"
"encoding/json"
)
type User struct {
name string
}
func (u *User) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Name string `json:"name"`
}{
Name: "customized" + u.name,
})
}
func main() {
user := &User{name: "Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(b))
}
Struct values encode as JSON objects. Each exported struct field becomes
a member of the object unless:
the field's tag is "-", or
the field is empty and its tag specifies the "omitempty" option.
The empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero. The object's default key string is the struct field name but can be specified in the struct field's tag value. The "json" key in the struct field's tag value is the key name, followed by an optional comma and options.