I am trying to Unmarshal the following JSON string into the struct below;
{
"io.confluent.connect.avro.ConnectDefault":{
"lastModifiedAt":{
"string":"2022-09-01T02:22:19+00:00"
},
"taxRateId":{
"int":5
},
"basedOn":{
"string":"Markup"
},
"priceTax":{
"double":2.04
},
"price":{
"int":24
},
"status":{
"string":"active"
},
"costPrice":{
"int":24
},
"createdAt":{
"string":"2022-09-01T02:22:19+00:00"
},
"productId":{
"int":3545
},
"ownershipId":{
"int":1
},
"dbId":{
"int":3655
},
"markupPercentage":{
"int":0
}
}
}
type Wrapper struct {
Message `json:"io.confluent.connect.avro.ConnectDefault"`
}
type Message struct {
DbId Field `json:"dbId"`
}
type Field struct {
Value map[string]interface{}
}
But it gives me an empty value for the Field map. Not sure what I am doing wrong here.
It's because you have extra level of nesting:
type Message struct {
DbId map[string]interface{} `json:"dbId"`
}
dbId property's value is a map of strings to anything.
Related
I'd like to get some input into how you would all go about unwinding nested response data into custom structs. Below is an example of the data i'm being returned. I'm trying to get to the user data.
{
"_links": {
"self": {
"href": "/api/v2/user-search/default/test?after=1585612800000&limit=20&offset=0&q=johnsmith%40test.com",
"type": "application/json"
}
},
"totalCount": 1,
"items": [
{
"lastPing": "2020-04-30T02:56:10.430867577Z",
"environmentId": "xxxx",
"ownerId": "xxxx",
"user": {
"key": "johnsmith#test.com",
"email": "johnsmith#test.com",
"firstName": "john",
"lastName": "smith"
},
"_links": {
"parent": {
"href": "/api/v2/users/default/test",
"type": "application/json"
},
"self": {
"href": "/api/v2/users/default/test/johnsmith#test.com",
"type": "application/json"
},
"settings": {
"href": "/api/v2/users/default/test/johnsmith#test.com/flags",
"type": "text/html"
},
"site": {
"href": "/default/test/users/johnsmith#test.com",
"type": "text/html"
}
}
}
]
}
Currently I'm doing the below
respData := map[string][]map[string]map[string]interface{}{}
json.Unmarshal(respBody, &respData)
userData := respData["items"][0]["user"]
I'd love to be able to unmarshal it into a custom struct but I can't seem to get it to work. The nested slice that the user object sits within is what keeps throwing me off.
type User struct {
Key string `json:"key"`
Email string `json:"email"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
}
type LinkInfo struct {
Href string `json:"href"`
Type string `json:"type"`
}
type Item struct {
LastPing time.Time `json:"lastPing"`
EnvironmentID string `json:"environmentId"`
OwnerID string `json:"ownerId"`
User User `json:"user"`
Links LinkInfo `json:"_links"`
Self LinkInfo `json:"self"`
Settings LinkInfo `json:"settings"`
Parent LinkInfo `json:"parent"`
Site LinkInfo `json:"site"`
}
type ItemDetails struct {
Links LinkInfo `json:"_links"`
TotalCount int `json:"total_count"`
Items []Item
}
Can you try this?
https://play.golang.org/p/S_CUN0XEh-d
From what you mentioned it sounds like you were on the right track. Your JSON is pretty large, so let me give you a smaller example similar to the part you mentioned you're having trouble with (the user object inside the items list).
type response struct {
TotalCount int `json:"totalCount"`
Items []*itemStruct `json:"items"`
}
type itemStruct struct {
LastPing string `json:"lastPing"`
User *userStruct `json:"user"`
}
type userStruct struct {
Key string `json:"key"`
}
Basically to map to a JSON list of objects, just put a field like this in your struct: Objects []*structWhichMapsToMyObject
Edit: Here's the code running in Go Playground: https://play.golang.org/p/EvSvv-2s8y8
If you want this:
"user": {
"key": "johnsmith#test.com",
"email": "johnsmith#test.com",
"firstName": "john",
"lastName": "smith"
}
Declare a matching Go struct:
type User struct {
Key string `json:"key"`
Email string `json:"email"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
}
Then, since the user's parent object looks like this:
"items": [
{
"lastPing": "2020-04-30T02:56:10.430867577Z",
"environmentId": "xxxx",
"ownerId": "xxxx",
"user": { ... },
"_links": { ... }
}
]
you also need to declare a matching Go struct for that (you can omit fields you don't need):
type Item struct {
User User `json:"user"`
}
and then the parent of the parent:
{
"_links": {
"self": {
"href": "/api/v2/user-search/default/test?after=1585612800000&limit=20&offset=0&q=johnsmith%40test.com",
"type": "application/json"
}
},
"totalCount": 1,
"items": [ ... ]
}
and the matching Go struct for the grandparent, again, include only the fields you need:
type ResponseData struct {
Items []Item `json:"items"`
}
Once you have this you can decode the json into an instance of ResponseData:
var rd ResponseData
if err := json.Unmarshal(data, &rd); err != nil {
panic(err)
}
for _, item := range rd.Items {
fmt.Println(item.User)
}
https://play.golang.com/p/7yavVSBcHQP
I'm decoding JSON using Decodable with init(from decoder:) but unable to parse out my nested JSON.
The JSON I'm trying to parse:
The main conflict I believe is parsing the array of edges.
The goal is to get a model I can use with key, value, and namespace.
Would need to use init(from decoder:).
{
"data": {
"collectionByHandle": {
"metafields": {
"edges": [
{
"node": {
"key": "city",
"value": "Fayetteville ",
"namespace": "shipping"
}
},
{
"node": {
"key": "country",
"value": "United States",
"namespace": "shipping"
}
},
{
"node": {
"key": "state",
"value": "AR",
"namespace": "shipping"
}
}
]
}
}
},
"errors": null
}
This technically works but wanting to use init(from decoder: ) and return a model that just has key, value, and namespace without having multiple separate structs/classes.
struct Shipping: Codable {
let data: DataClass
let errors: String?
}
struct DataClass: Codable {
let collectionByHandle: CollectionByHandle
}
struct CollectionByHandle: Codable {
let metafields: Metafields
}
struct Metafields: Codable {
let edges: [Edge]
}
struct Edge: Codable {
let node: Node
}
struct Node: Codable {
let key, value, namespace: String
}
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))
}
This is the struct I have:
type Resource struct {
Name string `json:"name"`
Ranges struct {
Range []struct {
Begin int `json:"begin"`
End int `json:"end"`
} `json:"range"`
} `json:"ranges,omitempty"`
Role string `json:"role,omitempty"`
Type string `json:"type"`
Scalar Scalar `json:"scalar,omitempty"`
}
I don't know how to make fields in the JSON not null. For example, struct Range like that:
{
"name": "cpus",
"ranges": {
"range": null
},
"type": "SCALAR",
"scalar": {
"value": 1
}
}, {
"name": "mem",
"ranges": {
"range": null
}
a way to do that is assign range as *string and then you should compare it with nil or not, if not nil convert it to string and marshall it again
That changes struct is resolved my problem:
type Resource struct {
Name string `json:"name"`
Ranges *Ranges `json:"ranges,omitempty"`
Role string `json:"role,omitempty"`
Type string `json:"type"`
Scalar *Scalar `json:"scalar,omitempty"`
}
assuming that you want to marshal the structs in your question and get a json output that looks like this:
{
"name": "cpus",
"ranges": {
"range": []
},
"type": "SCALAR",
"scalar": {
"value": 1
}
},
{
"name": "mem",
"ranges": {
"range": []
}
}
In golang slices [] are a reference type, that are backed by an array.
You can read up on the internals of slices here: https://blog.golang.org/go-slices-usage-and-internals
Basically the reason that you are getting null in the output is because you have not instantiated the slice, the slice is essentially a pointer, and that pointer is nil.
Create a new empty slice, like []Range{} and assign that to the field in Resource that is currently null in the json, and instead of a nil pointer, you will have an empty slice that will be marshalled as [] and not null.
I have the following response from a graph api
{
"data": [
{
"name": "Mohamed Galib",
"id": "502008940"
},
{
"name": "Mebin Joseph",
"id": "503453614"
},
{
"name": "Rohith Raveendranath",
"id": "507482441"
}
],
"paging": {
"next": "https://some_url"
}
}
I have a struct as follows
type Item struct {
Name, Id string
}
I wanted to parse the response and get an array of Item, How do I do that?
You need to update your struct like so:
type Item struct {
Name string `json:"name"`
Id string `json:"id"`
}
and add a struct to represent the wrapper:
type Data struct {
Data []Item `json:"data"`
}
You can then use json.Unmarshal to populate a Data instance.
See the example in the docs.