JSON string array without Keys into a GoLang struct - json

I am trying to parse JSON that is submitted from a POST request into a struct in a web service to send email. The following JSON is submitted in the body of the request.
{
"body": {
"template": "abctemplate.html",
"params": {
"name": "Chase",
"email": "1234#gmail.com"
}
},
"to": [
"abc#gmail.com",
"xyz#gmail.com"
],
"cc": [
"xxx#example.com",
"yyy#example.com"
],
"replyTo": {
"email": "aaa#gmail.com",
"name": "Jack"
},
"bcc": "ccc#gmail.com",
"subject": "Hello, world!"
}
This is mapped and read into the following struct
type emailData struct {
Body struct {
Template string `json:"template"`
Params map[string]string `json:"params"`
} `json:"body"`
To map[string]string `json:"To"` // TODO This is wrong
CC string `json:"cc"` // TODO this is wrong
ReplyTo struct {
Email string `json:"email"`
Name string `json:"name"`
}
BCC string `json:"bcc"`
Subject string `json:"subject"`
}
Both the 'to' and 'cc' JSON fields are string arrays of unknown length and do not have keys. Is there a way to map the string arrays into the struct fields? I've tried the two different ways where there are // TODO tags with no luck. Thanks!

Both cc and to are json arrays which you can unmarshal into Go slices without worrying about the length.
type emailData struct {
Body struct {
Template string `json:"template"`
Params map[string]string `json:"params"`
} `json:"body"`
To []string `json:"to"`
CC []string `json:"cc"`
ReplyTo struct {
Email string `json:"email"`
Name string `json:"name"`
}
BCC string `json:"bcc"`
Subject string `json:"subject"`
}
https://play.golang.org/p/Pi_5aSs922

Use the below to convert the json to struct in golang:
Json-goStruct
Take care of the maps, which might be go struct and vice-versa.

Related

Go Struct to decode expo push notification response?

I am making a service in go that sends push notification to Expo Backend. Once the http call is made Expo respond with bellow format(according to Expo):
{
"data": [
{
"status": "error" | "ok",
"id": string, // this is the Receipt ID
// if status === "error"
"message": string,
"details": JSON
},
...
],
// only populated if there was an error with the entire request
"errors": [{
"code": number,
"message": string
}]
}
And here is an provioded example of a response:
{
"data": [
{
"status": "error",
"message": "\\\"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]\\\" is not a registered push notification recipient",
"details": {
"error": "DeviceNotRegistered"
}
},
{
"status": "ok",
"id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
]
}
I created struct to decode the response.Now i get a error try to decode the reponse for the "details" field, No matter what type i use in my Struct. How can i deal with that field that expo mark as "JSON"?
import (
uuid "github.com/satori/go.uuid"
)
type PushResult struct {
Errors []ErrorDetails `json:"errors,omitempty"`
Datas []DataPart `json:"data,omitempty"`
}
type ErrorDetails struct {
Code int32 `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}
type DataPart struct {
Status string `json:"status,omitempty"`
ID uuid.UUID `json:"id,omitempty"`
Message string `json:"message,omitempty"`
Details string `json:"details,omitempty"` //also tried map[string]string and interface{}
}
This is the struct i am using. i also tried this by looking at the example:
type DataPart struct {
Status string `json:"status,omitempty"`
ID uuid.UUID `json:"id,omitempty"`
Message string `json:"message,omitempty"`
Details struct{
Error string `json:"error"`} `json:"details,omitempty"`
}
But every time get an error like "json: cannot unmarshal object into Go struct field DataPart.data.details"
I just created you struct using your response exemple and it worked completly fine.
Playground
That said if you wana a better way of debugging an "unmarshal error" you can unwrap it. That way you can print aditional data.
var t *json.UnmarshalTypeError
if errors.As(err, &t) {
spew.Dump(t)
}
Example -> I changed the error type to integer so I can fake the error.
OBS: Note that your "Datas" is an array and only the 1st one have an error.

Nested Go Structs for JSON marshaling with optional structs

I'm trying to initialize a nested struct to then marshal into json for an API response. The challenge I'm hitting is one of the components (a slice of structs) can have n number of members but of one of two possible types (text, image).
The JSON I want to create looks like this:
{
"messages": [
{
"message_parts": [
{
"text": {
"content": "dfdffd"
}
},
{
"image": {
"url": "https://image.jpg"
}
}
],
"actor_id": "44444444",
"actor_type": "agent"
}
],
"channel_id": "44444444",
"users": [
{
"id": "44444444"
}
]
}
In the message_parts slice, that can contain at least one of text or image but possibly one of each.
My structs look like this currently:
Type messagePayload struct {
Messages []Messages `json:"messages"`
Status string `json:"status,omitempty"`
ChannelID string `json:"channel_id"`
Users []Users `json:"users"`
}
type Messages struct {
MessageParts []MessageParts `json:"message_parts"`
ActorID string `json:"actor_id"`
ActorType string `json:"actor_type"`
}
type Users struct {
ID string `json:"id"`
}
type Text struct {
Content string `json:"content,omitempty"`
}
type MessageParts struct {
Text *Text `json:"text,omitempty"`
Image *Image `json:"image,omitempty"`
}
type Image struct {
URL string `json:"url,omitempty"`
}
I'm really struggling to initialize this in a way that not show up in the json if they're not present.
here's where I'm at but it obviously doesn't work:
payload := &messagePayload{
Messages: []Messages{
{
MessageParts: []MessageParts{
{
&Text{
Content: text,
},
},
{
&Image{
URL: mediaurl,
},
},
},
ActorID: agentID,
ActorType: "agent",
}},
ChannelID: channelid,
Users: []Users{
{
ID: user,
},
},
}
EDIT:
Thanks to hint below and a few other findings, I've found the best way is to initalize the payload and then add the slices for text and images as needed:
https://play.golang.org/p/Pmmv00spcI6
As noted above, I was able to find a solution- you need to initialize the payload without the text or image data, then append them to the MessageParts slice:
package main
import (
"encoding/json"
"fmt"
)
type messagePayload struct {
Messages []Messages `json:"messages"`
Status string `json:"status,omitempty"`
ChannelID string `json:"channel_id"`
Users []Users `json:"users"`
}
type Messages struct {
MessageParts []MessageParts `json:"message_parts"`
ActorID string `json:"actor_id"`
ActorType string `json:"actor_type"`
}
type Users struct {
ID string `json:"id"`
}
type Text struct {
Content string `json:"content,omitempty"`
}
type MessageParts struct {
Text *Text `json:"text,omitempty"`
Image *Image `json:"image,omitempty"`
}
type Image struct {
URL string `json:"url,omitempty"`
}
func main() {
payload := &messagePayload{
Messages: []Messages{
{
MessageParts: []MessageParts{
},
ActorID: "id",
ActorType: "agent",
}},
ChannelID: "cid",
Users: []Users{
{
ID: "user1",
},
},
}
var text= new(MessageParts)
text.Text = &Text{Content: "LOL"}
var image = new(MessageParts)
image.Image= &Image{URL: "https://"}
payload.Messages[0].MessageParts = append(payload.Messages[0].MessageParts, *text)
payload.Messages[0].MessageParts = append(payload.Messages[0].MessageParts, *image)
m, err := json.Marshal(payload)
if err != nil {
fmt.Println("Error, ", err)
return
}
fmt.Println(string(m))
}

json object with nested structures in golang

I have the following json object that I am trying to represent with Go as a type JsonObject struct
and pass it back in its original json so that I can return the json as an api endpoint. Any advice/example?
[{
"time": 173000,
"id": "VLSuEE5m1kmIhgE7ZhHDFe",
"height": "",
"DATASTRUCTURE": {
},
"language": "en",
"size": 0,
"url": "http://www.gstatic.com/play.m3u8",
"type": "vid",
"definitionid": "h264",
"reference": "PAN-EN",
"content": "This is some content",
"revisiondate": "2017-11-29T00:00:00",
"data": {
},
"id": "BBBB3424-153E-49DE-4786-013B6611BBBB",
"thumbs": {
"32": "https://www.gstatic.com/images?q=tbn:ANd9GcRj",
"64": "https://www.gstatic.com/images?q=tbn:DPd3GcRj"
},
"title": "Cafeteria",
"hash": "BBBB5d39bea20edf76c94133be61BBBB"
}]
You can use https://mholt.github.io/json-to-go/ to generate struct for the given json schema.
For example, json given in the question can be represented like:
type AutoGenerated []struct {
Time int `json:"time"`
ID string `json:"id"`
Height string `json:"height"`
DATASTRUCTURE struct {
} `json:"DATASTRUCTURE"`
Language string `json:"language"`
Size int `json:"size"`
URL string `json:"url"`
Type string `json:"type"`
Definitionid string `json:"definitionid"`
Reference string `json:"reference"`
Content string `json:"content"`
Revisiondate string `json:"revisiondate"`
Data struct {
} `json:"data"`
Thumbs struct {
Num32 string `json:"32"`
Num64 string `json:"64"`
} `json:"thumbs"`
Title string `json:"title"`
Hash string `json:"hash"`}
Hope this helps!

How do I parse a nested array in a nested JSON object in Golang?

I have a JSON:
{
"data": [
{
"id": 1,
"values": [
[
{
"id": "11",
"keys": [
{
"id": "111"
}
]
}
]
]
}
]
}
I want to parse "values" and "keys" into structs, but I don't known what type should i use in "Data"?:
type Value struct {
Id string `json:"id"`
Keys []Key `json:"keys"`
}
type Key struct {
Id string `json:"id"`
}
type Result struct {
Data []Data `json:"data"`
}
type Data struct {
Id int `json:"id"`
Values []???? `json:"values"`
}
I would be grateful for any help. Thanks.
If you look carefully at your json. you have an array in an array...
...
"values": [
[...
If this is intended then the type of values is:
[][]Value
to represent the two arrays, else remove the array nesting and it becomes:
[]Value
Runnable Example: https://play.golang.org/p/UUqQR1KSwB
type Basic struct {
ID string `json:"id"`
}
type Inner struct {
ID string `json:"id"`
Keys []Basic `json:"keys"`
}
type Middle struct {
ID int `json:"id"`
Values []Inner `json:"values"`
}
type Final struct {
Data []Middle `json:"data"`
}

Parsing JSON in GoLang into struct

So, I'm having some trouble parsing this data in golang:
{
"gateways": [
{
"token": "my_token_here",
"gateway_type": "test",
"description": null,
"payment_methods": [
"credit_card",
"sprel",
"third_party_token",
"bank_account",
"apple_pay"
],
"state": "retained",
"created_at": "2016-03-12T18:52:37Z",
"updated_at": "2016-03-12T18:52:37Z",
"name": "Spreedly Test",
"characteristics": [
"purchase",
"authorize",
"capture",
"credit",
"general_credit",
"void",
"verify",
"reference_purchase",
"purchase_via_preauthorization",
"offsite_purchase",
"offsite_authorize",
"3dsecure_purchase",
"3dsecure_authorize",
"store",
"remove",
"disburse",
"reference_authorization"
],
"credentials": [],
"gateway_specific_fields": [],
"redacted": false
}
]
}
When using this struct I can get it to output pretty easily.
type gateways struct {
Gateways []struct {
Characteristics []string `json:"characteristics"`
CreatedAt string `json:"created_at"`
Credentials []interface{} `json:"credentials"`
Description interface{} `json:"description"`
GatewaySpecificFields []interface{} `json:"gateway_specific_fields"`
GatewayType string `json:"gateway_type"`
Name string `json:"name"`
PaymentMethods []string `json:"payment_methods"`
Redacted bool `json:"redacted"`
State string `json:"state"`
Token string `json:"token"`
UpdatedAt string `json:"updated_at"`
} `json:"gateways"`
}
But as soon as I seperate the "Gateways []struct" into its own struct then it returns an empty array...
Full source.
type gateway struct {
Characteristics []string `json:"characteristics"`
CreatedAt string `json:"created_at"`
Credentials []interface{} `json:"credentials"`
Description interface{} `json:"description"`
GatewaySpecificFields []interface{} `json:"gateway_specific_fields"`
GatewayType string `json:"gateway_type"`
Name string `json:"name"`
PaymentMethods []string `json:"payment_methods"`
Redacted bool `json:"redacted"`
State string `json:"state"`
Token string `json:"token"`
UpdatedAt string `json:"updated_at"`
}
type gateways struct {
Gateways []gateway `json:"gateways"`
}
func ParseResponse() {
var parsed gateways
json.Unmarshal(json, &parsed)
}
There's a problem with your ParseResponse function, you're calling json.Unmarshal passing as first parameter json, that's a packge name: that's ambiguous.
As you can see, your code works well changing the ParseResponse function.
package main
import (
"encoding/json"
"fmt"
)
type gateway struct {
Characteristics []string `json:"characteristics"`
CreatedAt string `json:"created_at"`
Credentials []interface{} `json:"credentials"`
Description interface{} `json:"description"`
GatewaySpecificFields []interface{} `json:"gateway_specific_fields"`
GatewayType string `json:"gateway_type"`
Name string `json:"name"`
PaymentMethods []string `json:"payment_methods"`
Redacted bool `json:"redacted"`
State string `json:"state"`
Token string `json:"token"`
UpdatedAt string `json:"updated_at"`
}
type gateways struct {
Gateways []gateway `json:"gateways"`
}
func ParseResponse(js []byte) {
var parsed gateways
json.Unmarshal(js, &parsed)
fmt.Println(parsed)
}
func main() {
var js []byte = []byte(`{
"gateways": [
{
"token": "my_token_here",
"gateway_type": "test",
"description": null,
"payment_methods": [
"credit_card",
"sprel",
"third_party_token",
"bank_account",
"apple_pay"
],
"state": "retained",
"created_at": "2016-03-12T18:52:37Z",
"updated_at": "2016-03-12T18:52:37Z",
"name": "Spreedly Test",
"characteristics": [
"purchase",
"authorize",
"capture",
"credit",
"general_credit",
"void",
"verify",
"reference_purchase",
"purchase_via_preauthorization",
"offsite_purchase",
"offsite_authorize",
"3dsecure_purchase",
"3dsecure_authorize",
"store",
"remove",
"disburse",
"reference_authorization"
],
"credentials": [],
"gateway_specific_fields": [],
"redacted": false
}
]
}`)
/*
var parsed gateways
e := json.Unmarshal(js, &parsed)
if e != nil {
fmt.Println(e.Error())
} else {
fmt.Println(parsed)
}
*/
ParseResponse(js)
}
Outputs:
{[{[purchase authorize capture credit general_credit void verify reference_purchase purchase_via_preauthorization offsite_purchase offsite_authorize 3dsecure_purchase 3dsecure_authorize store remove disburse reference_authorization] 2016-03-12T18:52:37Z [] <nil> [] test Spreedly Test [credit_card sprel third_party_token bank_account apple_pay] false retained my_token_here 2016-03-12T18:52:37Z}]}
Take a look at http://play.golang.org/p/3xJHBmhuei - unmarshalling into your second definition of gateways completes successfully, so it must be something little that you're missing.
Check if the json.Unmarshal call returns an error.
P.S. It probably isn't the problem if you're unmarshalling successfully into the first version of gateways, but the JSON string that you've given above is missing a closing "}" bracket.