Golang Struct Won't Marshal to JSON [duplicate] - json

This question already has answers here:
My structures are not marshalling into json [duplicate]
(3 answers)
Closed 7 years ago.
I'm trying to marshal a struct in Go to JSON but it won't marshal and I can't understand why.
My struct definitions
type PodsCondensed struct {
pods []PodCondensed `json:"pods"`
}
func (p *PodsCondensed) AddPod(pod PodCondensed) {
p.pods = append(p.pods, pod)
}
type PodCondensed struct {
name string `json:"name"`
colors []string `json:"colors"`
}
Creating and marshaling a test struct
fake_pods := PodsCondensed{}
fake_pod := PodCondensed {
name: "tier2",
colors: []string{"blue", "green"},
}
fake_pods.AddPod(fake_pod)
fmt.Println(fake_pods.pods)
jPods, _ := json.Marshal(fake_pods)
fmt.Println(string(jPods))
Output
[{tier2 [blue green]}]
{}
I'm not sure what the issue is here, I export json data for all my structs, the data is being stored correctly and is available to print. It just wont marshal which is odd because everything contained in the struct can be marshaled to JSON on its own.

This is a common mistake: you did not export values in the PodsCondensed and PodCondensed structures, so the json package was not able to use it. Use a capital letter in the variable name to do so:
type PodsCondensed struct {
Pods []PodCondensed `json:"pods"`
}
type PodCondensed struct {
Name string `json:"name"`
Colors []string `json:"colors"`
}
Working example: http://play.golang.org/p/Lg3cTO7DVk
Documentation: http://golang.org/pkg/encoding/json/#Marshal

Related

How to represent Golang struct with variable field names [duplicate]

This question already has answers here:
How to Unmarshal jSON with dynamic key which can't be captured as a `json` in struct: GOlang [duplicate]
(1 answer)
How to parse/deserialize dynamic JSON
(4 answers)
Closed 8 months ago.
If this has been answered somewhere else let me know but I couldn't find anything.
Basically, let's say we have the following JSON. string-id-001 can be an arbitrary string. We want to unmarshal it into a struct, and be able to access the unique id's.
{"list":{"string-id-001":{"id":"blah","name":"cool"},"string-id-002":{"id":"yas","name":"rad"}}}
Golang as far as I can tell would require something like below which doesn't work if the keyhere value is constantly changing. Eg if it's an ID
type Foo struct {
List struct {
StringID001 struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"string-id-001"`
StringID002 struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"string-id-002"`
} `json:"list"`
}
I've seen a similar issue in another project (which I solved with interfaces rather than structs), and I'm wondering if there's a nicer solution. Am I missing something obvious?
type payLoad struct {
ID string `json:"id"`
Name string `json:"name"`
}
type Foo struct {
List map[string]payLoad `json:"list"`
}

Representing a list of things of different type in JSON and Golang [duplicate]

This question already has answers here:
Is it possible to partially decode and update JSON? (go)
(2 answers)
How to parse a complicated JSON with Go unmarshal?
(3 answers)
golang | unmarshalling arbitrary data
(2 answers)
How to parse JSON in golang without unmarshaling twice
(3 answers)
Decoding generic JSON objects to one of many formats
(1 answer)
Closed 9 months ago.
I'm trying to represent some data in JSON where there is a list of things where each thing has some common features (ex name) and another field who's value can either be a string or an integer. For example:
{
"items": [
{
"name": "thing1",
"type": "string",
"value": "foo"
},
{
"name": "thing2",
"type": "int",
"value": 42
}
]
}
That JSON looks reasonable to me, but trying to create a data structure to deserialize (unmarshal) it into in Golang is proving difficult. I think I could do it in Java with class polymorphism, but in Go I feel trapped. I've tried many things but haven't got it. Ultimately, it comes down to lack of struct type polymorphism.
In Go, I can have a slice (list) of interfaces, but I need actual structs of different types as far as I can tell.
Any suggestions on how to represent this in Golang, and be able to unmarshal into?
Or alternatively, should I structure the JSON itself differently?
Thanks!
You can create a structure like this
type items struct {
name string
type_1 string
value interface{}
}
Interface can hold any data type, also as type is reserved keyword i have used type_1
You can do it in Go 1.18 like this:
type Data struct {
Items []struct {
Name string `json:"name"`
Type string `json:"type"`
Value any `json:"value"`
} `json:"items"`
}
func main() {
data := Data{}
// it's your json bytes
bytesData := []byte()
if err := json.Unmarshal(byteData, &data); err != nil {
log.Fatal(err)
}
}
// use data here
P.S. if you are using older versions use interface{} instead of any.
This data structure properly represents your JSON:
type Data struct {
Items []struct {
Name string `json:"name"`
Type string `json:"type"`
Value interface{} `json:"value"`
} `json:"items"`
}
Then, you can use json.Unmarshal.
If you use Go 1.18, you can use any alias of interface{}.
In addition, in Go you don't even need a type field. You can use Type assertions to determine the value type.

[Go]: Parsing JSON [duplicate]

This question already has answers here:
Lowercase JSON key names with JSON Marshal in Go
(4 answers)
Closed 5 years ago.
What I am trying to do
I am parsing a JSON HTTP response based on this answer to a similar question. My code is able to parse the JSON without any error but is unable to read the values and store them in the provided variable.
This has been puzzling me for the last 2 hours and it might be due to a trivial reason that I am overlooking here.
CODE
type ImporterResponse struct {
results []packagemeta `json:"results"`
}
type packagemeta struct {
path string `json:"path"`
synopsis string `json:"synopsis,omitempty"`
count int `json:"import_count,omitempty`
}
func main() {
res := []byte(`{"results":[{"path":"4d63.com/randstr/lib/randstr","import_count":0,"synopsis":"Package randstr generates random strings (e.g."},{"path":"bitbucket.org/pcas/tool/mathutil","import_count":0}]}`)
fmt.Println("Decoding the JSON")
r := bytes.NewReader(res)
decoder := json.NewDecoder(r)
packageimporters := &ImporterResponse{}
err := decoder.Decode(packageimporters)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Packageimporters: %+v", packageimporters)
fmt.Println(len(packageimporters.results))
}
Link to Playground: https://play.golang.org/p/NzLl7Ujo2IJ
What I want:
How to fix this?
Why is no error message raised if JSON is not parsed properly?
P.S: I understand that this question has been asked before and there are possible solutions available but none of them work for me. Hence, I have made this post.
You need to make your struct fields exported, otherwise the json package cannot access them.
Please read JSON and go for more details, specifically this paragraph:
The json package only accesses the exported fields of struct types
(those that begin with an uppercase letter). Therefore only the the
exported fields of a struct will be present in the JSON output.
And this one for more details:
How does Unmarshal identify the fields in which to store the decoded
data? For a given JSON key "Foo", Unmarshal will look through the
destination struct's fields to find (in order of preference):
An exported field with a tag of "Foo" (see the Go spec for more on
struct tags),
An exported field named "Foo", or
An exported field
named "FOO" or "FoO" or some other case-insensitive match of "Foo".
So your struct should really be:
type Packagemeta struct {
Path string `json:"path"`
Synopsis string `json:"synopsis,omitempty"`
Count int `json:"import_count,omitempty`
}

unmarshal generic json in Go [duplicate]

This question already has answers here:
JSON and dealing with unexported fields
(2 answers)
(un)marshalling json golang not working
(3 answers)
json.Marshal(struct) returns "{}"
(3 answers)
Printing Empty Json as a result [duplicate]
(1 answer)
Parsing JSON in Golang doesn't Populate Object [duplicate]
(1 answer)
Closed 10 months ago.
I'm a new Go programmer (From Java) and I would like to reproduce a generic way which is esay to use in Java.
I want to create some function which allow me to do an Unmarshal on a JSON string in order to avoid code duplicity.
This is my current code which is not working :
type myStruct1 struct {
id string
name string
}
func (obj myStruct1) toString() string {
var result bytes.Buffer
result.WriteString("id : ")
result.WriteString(obj.id)
result.WriteString("\n")
result.WriteString("name : ")
result.WriteString(obj.name)
return result.String()
}
func main() {
content := `{id:"id1",name="myName"}`
object := myStruct1{}
parseJSON(content, object)
fmt.Println(object.toString())
}
func parseJSON(content string, object interface{}) {
var parsed interface{}
json.Unmarshal([]byte(content), &parsed)
}
This code, on run, returns me this :
id :
name :
Do you have any idea ?
Thanks
The issue is you want to write to a generic type? You probably want a string map. This works with BSON anyways:
var anyJson map[string]interface{}
json.Unmarshal(bytes, &anyJson)
You'll be able to access the fields like so:
anyJson["id"].(string)
Don't forget to type assert your values, and they must be the correct type or they'll panic. (You can read more about type assertions on the golang site)
To parse "generic JSON" when you have no idea what schema it has:
var parsed any
err := json.Unmarshal(jsonText, &parsed)
The returned any in parsed will be a map[string]any or []any or nil or single values float64, bool, string.
You can test the type and react accordingly.
import (
"encoding/json"
"fmt"
)
func test(jsonText []byte) {
// parsing
var parsed any
err := json.Unmarshal(jsonText, &parsed)
if err != nil {
panic(err) // malformed input
}
// type-specific logic
switch val := parsed.(type) {
case nil:
fmt.Println("json specifies null")
case map[string]any:
fmt.Printf("id:%s name:%s\n", val["id"], val["name"])
case []any:
fmt.Printf("list of %d items\n", len(val))
case float64:
fmt.Printf("single number %f\n", val)
case bool:
fmt.Printf("single bool %v\n", val)
case string:
fmt.Printf("single string %s\n", val)
default:
panic(fmt.Errorf("type %T unexpected", parsed))
}
}
Unmarshal will only set exported fields of the struct.
Which means you need to modify the json struct to use capital case letters:
type myStruct1 struct {
Id string
Name string
}
The reason behind this is that the json library does not have the ability to view fields using reflect unless they are exported.
You have to export your fields:
type myStruct1 struct {
Id string
Name string
}
See Exported Identifiers from documentation.
There are a few changes you need to make in your code to make it work:
The function json.Unmarshal can only set variables inside your struct which are exported, that is, which start with capital letters. Use something like ID and Name for your variable names inside myStruct1.
Your content is invalid JSON. What you actually want is {"ID":"id1","Name":"myName"}.
You're passing object to parseJSON but you're using parsed instead, not object. Make parseJSON receive a *myStruct (instead of an interface{}), and use that variable instead of parsed when unmarshalling the string. Also, always handle the error returns, like err := json.Unmarshal(content, object), and check err.
I'd suggest you to do the Golang Tour ;)
You can also set the file as an Object with dynamic properties inside another struct. This will let you add metadata and you read it the same way.
type MyFile struct {
Version string
Data map[string]interface{}
}

JSON Unmarshall not working as expected with structs [duplicate]

This question already has answers here:
My structures are not marshalling into json [duplicate]
(3 answers)
Closed 7 years ago.
I have the following code:
package main
import "encoding/json"
import "fmt"
type SuperNum struct {
num string
}
func main() {
byt := []byte(`{"num":"6.13"}`)
var dat SuperNum
if err := json.Unmarshal(byt, &dat); err != nil {
panic(err)
}
fmt.Printf("%+v", dat) // I expect this to have a `num` attribute
}
Output:
{num:}
Program exited.
You can run this code in the golang playground.
Because I'm setting a num property in the struct and in the JSON and they're both strings, I would have expected the dat struct to have a num property, with 'hello', but it doesn't.
What am I doing wrong? What in my mental model of how this should work is incorrect?
EDIT
I tried adding the json signature to the struct, but it makes no difference (no idea what that actually does).
type SuperNum struct {
num string `json:"num"`
}
num is by convention not exported as it is lower case. Change it to Num and you are able to inspect the result.
type SuperNum struct {
Num string
}
Just change num to Num. The lowercase properties of the structures are not visible.
Go playground
When unmarhalling JSON structures, the properties that you're mapping on must be public (remember that in Go, public and private visibility of struct and module members is denoted by the member's name being upper or lower camel case.
So, first of all, your struct must be defined like this:
type SuperNum struct {
Num string // <- note the capital "N"
}
With this struct, the JSON marshaller will expect the JSON property to be also named Num. In order to configure a different property name (like the lowercased num in your example), use the json annotation for that struct member:
type SuperNum struct {
Num string `json:"num"`
}