Go return json variables without capitals [duplicate] - json

This question already has answers here:
Lowercase JSON key names with JSON Marshal in Go
(4 answers)
Closed 5 years ago.
I'm a new user converting an application to Go. I have something like the following which is working:
type Network struct {
Ssid string
Security string
Bitrate string
}
func Scan(w http.ResponseWriter, r *http.Request) {
output := runcmd(scripts+"scan.sh", true)
bytes := []byte(output)
var networks []Network
json.Unmarshal(bytes, &networks)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(networks)
}
The problem is the old version didn't use capitals on the json variables returned.
I want the front-end to see ssid not Ssid. If I make the attributes in the struct lowercase the code no longer works as they become unexported variables.

When the field names in your struct do not match the json field names, you can use field tags.
eg:
Ssid string `json:"myOtherFieldName"`
Please read the json docs for more details.

This tool very convenient for learning:
https://mholt.github.io/json-to-go/
Give it example of JSON that you want it will recommend golang.
e.g. JSON
{
"ssid": "some very long ssid",
"security": "big secret",
"bitrate": 1024
}
will suggest golang:
type AutoGenerated struct {
Ssid string `json:"ssid"`
Security string `json:"security"`
Bitrate int `json:"bitrate"`
}
now you can change AutogGenerated, Ssid, Security, Bitrate to whatever you want.

Related

How to unmarshal raw CSV response into a struct? [duplicate]

This question already has an answer here:
Convert byte slice to io.Reader
(1 answer)
Closed 2 months ago.
I have a body response that I can only get Byte responses. This bytes encode a csv-like response. Something like:
element_a,element_b,element_c
cooper,claus,active
carlos,saldanha,inactive
robert,jesus,active
Lets say then that I have the struct that looks like this:
type ESResponse struct {
ElementA string `csv:"element_a"`
ElementB string `csv:"element_b"`
ElementC string `csv:"element_c"`
}
I would like to unmarshal the byte response so then I'm able to access its elements.
What I've been doing is the following:
var actualResult ESResponse
body := util.GetResponseBody() // this is where the byte response comes from.
in := string(body[:]) // here I transform it to a string but I trully think this is not the best way.
err = gocsv.Unmarshal(in, &actualResult)
I've been using this library here: https://pkg.go.dev/github.com/gocarina/gocsv#section-readme but I'm unable to understand the error I get which is:
cannot use in (variable of type string) as io.Reader value in argument to gocsv.Unmarshal: string does not implement io.Reader (missing method Read)
It means, that in argument must implement interface io.Reader, but you argument's type is string, which doesn't. So if you want to deserialize value from string, you can do this:
body := `
element_a,element_b,element_c
cooper,claus,active
carlos,saldanha,inactive
robert,jesus,active`
var actualResult []ESResponse
in := strings.NewReader(body)
err := gocsv.Unmarshal(in, &actualResult)
or gocsv.Unmarshal(bytes.NewReader([]byte(body)), &actualResult) to deserialize from bytes array

Go sum type in json [duplicate]

This question already has answers here:
How can we read a json file as json object in golang
(2 answers)
Closed 7 months ago.
What I want to achieve
I'm parsing IAM Policies in Go. In IAM Policies, most of the fields can be either a string or an array of strings. It's hard to think these decision trees in my head what I want is kind of exhaustive pattern matching.
What I did
I loeded a policy with json.Unmarshal
type Policy struct {
Version string `json:"Version"`
Id string `json:"ID,omitempty"`
Statements []Statement `json:"Statement"`
}
type Statement struct {
// ...
Action interface{} `json:"Action"` // string or array
// ...
}
And iterating over statements.
switch ele := st.Action.(type) {
case string:
action, _ := expandAction(ele, data)
actions = append(actions, action...)
setter(i, actions)
case []string:
for _, a := range ele {
if strings.Contains(a, "*") {
exps, _ := expandAction(a, data)
actions = append(actions, exps...)
} else {
actions = append(actions, a)
}
setter(i, actions)
fmt.Println(actions)
}
default:
// interface{}
}
The Problem
It always goes to the default case.
Can use reflection, but don't think I really should, since runtime could know types when json.Unnarshal is called.
As you can see from the official document the type for JSON array is []interface. If you update []string to []interface then you can run the related case block. However, if you have to sure that is array of string, reflection can provide it.

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.

Read request body two times in Golang [duplicate]

This question already has answers here:
How to read multiple times from same io.Reader
(5 answers)
Closed 4 years ago.
type ValidationModel struct {
Name string `json:"name" valid:"alpha,required~Name is required"`
Email string `json:"email" valid:"email~Enter a valid email.,required~Email is required."`
Password string `json:"password" valid:"required~Password is required"`
}
validationModel := ValidationModel{}
json.NewDecoder(r.Body).Decode(&validationModel)
_, err := govalidator.ValidateStruct(validationModel)
First I am validating the request body using govalidator.
type UserModel struct {
ID bson.ObjectId `json:"_id" bson:"_id"`
Name string `json:"name" bson:"name"`
Email string `json:"email" bson:"email"`
Password string `json:"password,omitempty" bson:"-"`
PasswordHash string `json:"-" bson:"passwordHash"`
Salt string `json:"-" bson:"salt"`
Token string `json:"token,omitempty" bson:"-"`
}
user := models.UserModel{}
json.NewDecoder(r.Body).Decode(&user)
fmt.Println(user)
And after validating the request, again I am decoding the request body into user struct, but the request body has been read once using validationModel, so when I try to again decode it into user, it is not giving me any values.
I can think of two solutions here:
Store request body in one separate variable, and use that variable two times.
Copy validationModel values in user.
But I don't have any idea about to implement these approaches and which approach is best to follow. Or is there any other solution which can be implemented?
Thanks in advance.
Storing the data can be easily done with ioutil.ReadAll():
data, err := ioutil.ReadAll(r.Body)
If you need the data back as a io.Reader (which is how the r.Body is), then you can use bytes.NewReader():
reader := bytes.NewReader(data)
And ACTUALLY, r.Body is a io.ReadCloser, so if you need that you can use ioutil.NopCloser() in conjunction with bytes.NewReader():
reader := ioutil.NopCloser(bytes.NewReader(data))

[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`
}