Parse Error from Converting Json String to Struct - json

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
}

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))
}

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)
}

Unmarshalling json to structure using json.RawMessage

I need to unmarshal json object which may have the following formats:
Format1:
{
"contactType": 2,
"value": "0123456789"
}
Format2:
{
"contactType": "MobileNumber",
"value": "0123456789"
}
The structure I'm using for unmarshalling is:-
type Contact struct {
ContactType int `json:"contactType"`
Value string `json:"value"`
}
But this works only for format 1. I don't want to change the datatype of ContactType but I want to accommodate the 2nd format as well. I heard about json.RawMarshal and tried using it.
type Contact struct {
ContactType int
Value string `json:"value"`
Type json.RawMessage `json:"contactType"`
}
type StringContact struct {
Type string `json:"contactType"`
}
type IntContact struct {
Type int `json:"contactType"`
}
This gets the unmarshalling done, but I'm unable to set the ContactType variable which depends on the type of json.RawMessage. How do I model my structure so that this problem gets solved?
You will need to do the unmarshalling yourself. There is a very good article that shows how to use the json.RawMessage right and a number of other solutions to this very problem, Like using interfaces, RawMessage, implemention your own unmarshal and decode functions etc.
You will find the article here: JSON decoding in GO by Attila Oláh
Note: Attila has made a few errors on his code examples.
I taken the liberty to put together (using some of the code from Attila) a working example using RawMessage to delay the unmarshaling so we can do it on our own version of the Decode func.
Link to GOLANG Playground
package main
import (
"fmt"
"encoding/json"
"io"
)
type Record struct {
AuthorRaw json.RawMessage `json:"author"`
Title string `json:"title"`
URL string `json:"url"`
Author Author
}
type Author struct {
ID uint64 `json:"id"`
Email string `json:"email"`
}
func Decode(r io.Reader) (x *Record, err error) {
x = new(Record)
if err = json.NewDecoder(r).Decode(x); err != nil {
return
}
if err = json.Unmarshal(x.AuthorRaw, &x.Author); err == nil {
return
}
var s string
if err = json.Unmarshal(x.AuthorRaw, &s); err == nil {
x.Author.Email = s
return
}
var n uint64
if err = json.Unmarshal(x.AuthorRaw, &n); err == nil {
x.Author.ID = n
}
return
}
func main() {
byt_1 := []byte(`{"author": 2,"title": "some things","url": "https://stackoverflow.com"}`)
byt_2 := []byte(`{"author": "Mad Scientist","title": "some things","url": "https://stackoverflow.com"}`)
var dat Record
if err := json.Unmarshal(byt_1, &dat); err != nil {
panic(err)
}
fmt.Printf("%#s\r\n", dat)
if err := json.Unmarshal(byt_2, &dat); err != nil {
panic(err)
}
fmt.Printf("%#s\r\n", dat)
}
Hope this helps.

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.