How to unmarshal this json string - json

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.

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
}

Converting Json to string in golang

What I am trying to do is to convert the JSON response I got from a third party API to string to be able to render it on the webpage. My attempt first was to create a struct called money which holds the 3 values which are being returned and then Unmarshel the bytes but I don't get anything displayed
Here is the struct
type money struct {
Base string `json:"base"`
Currency string `json:"currency"`
Amount float32 `json:"amount"`}
and inside the getCurrency() func
response, err := http.Get("https://api.coinbase.com/v2/prices/spot?currency=USD")
if err != nil {
fmt.Printf("The http requst failed with error %s \n", err)
} else {
answer, _ := ioutil.ReadAll(response.Body)
response := money{}
json.Unmarshal([]byte(answer), &response)
fmt.Fprintln(w, response)
fmt.Fprintln(w, response.Currency)
}
Finally here is what i get from the json response
{"data":{"base":"BTC","currency":"USD","amount":"4225.87"}}
I had to remove the double quotes from the 'amount' value in order to allow the parsing into float32:
{"data":{"base":"BTC","currency":"USD","amount":4225.87}}
See on Playground: https://play.golang.org/p/4QVclgjrtyi
Full code:
package main
import (
"encoding/json"
"fmt"
)
type money struct {
Base string `json:"base"`
Currency string `json:"currency"`
Amount float32 `json:"amount"`
}
type info struct {
Data money
}
func main() {
str := `{"data":{"base":"BTC","currency":"USD","amount":4225.87}}`
var i info
if err := json.Unmarshal([]byte(str), &i); err != nil {
fmt.Println("ugh: ", err)
}
fmt.Println("info: ", i)
fmt.Println("currency: ", i.Data.Currency)
}

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

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