JSON encode Go struct with a proto child [duplicate] - go

type TestObject struct {
kind string `json:"kind"`
id string `json:"id, omitempty"`
name string `json:"name"`
email string `json:"email"`
}
func TestCreateSingleItemResponse(t *testing.T) {
testObject := new(TestObject)
testObject.kind = "TestObject"
testObject.id = "f73h5jf8"
testObject.name = "Yuri Gagarin"
testObject.email = "Yuri.Gagarin#Vostok.com"
fmt.Println(testObject)
b, err := json.Marshal(testObject)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(b[:]))
}
Here is the output:
[ `go test -test.run="^TestCreateSingleItemResponse$"` | done: 2.195666095s ]
{TestObject f73h5jf8 Yuri Gagarin Yuri.Gagarin#Vostok.com}
{}
PASS
Why is the JSON essentially empty?

You need to export the fields in TestObject by capitalizing the first letter in the field name. Change kind to Kind and so on.
type TestObject struct {
Kind string `json:"kind"`
Id string `json:"id,omitempty"`
Name string `json:"name"`
Email string `json:"email"`
}
The encoding/json package and similar packages ignore unexported fields.
The `json:"..."` strings that follow the field declarations are struct tags. The tags in this struct set the names of the struct's fields when marshaling to and from JSON.
Ru it on the playground.

When the first letter is capitalised, the identifier is public to any
piece of code that you want to use.
When the first letter is lowercase, the identifier is private and
could only be accessed within the package it was declared.
Examples
var aName // private
var BigBro // public (exported)
var 123abc // illegal
func (p *Person) SetEmail(email string) { // public because SetEmail() function starts with upper case
p.email = email
}
func (p Person) email() string { // private because email() function starts with lower case
return p.email
}

In golang
in struct first letter must uppercase
ex. phonenumber -> PhoneNumber
======= Add detail
First, I'm try coding like this
type Questions struct {
id string
questionDesc string
questionID string
ans string
choices struct {
choice1 string
choice2 string
choice3 string
choice4 string
}
}
golang compile is not error and not show warning. But response is empty because something
After that, I search google found this article
Struct Types and Struct Type Literals
Article then... I try edit code.
//Questions map field name like database
type Questions struct {
ID string
QuestionDesc string
QuestionID string
Ans string
Choices struct {
Choice1 string
Choice2 string
Choice3 string
Choice4 string
}
}
Is work.
Hope for help.

Related

Transform values when umarshaling

Given the code below, is it possible to transform first name while it's being unmarshaled? Say I want to always have it be lowercase whether it is in the actual json or not.
type Person struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
func main() {
jsonText := GetJsonFromSomewhere()
var v Person
json.Unmarshal(jsonText, &v)
}
One way to do this is to create a custom type that implements the Unmarshaler interface from the encoding/json package. Here's a link to this interface. Any type which implements Unmarshaler can be used as the type of a struct field when doing JSON unmarshalling. When doing the unmarshalling, encoding/json will use your implementation of the interface's UnmarshalJSON function to convert the JSON bytes to the field type.
So you could write an UnmarshalJSON function which includes changing the string values to lowercase.
Here's an example of what that could look like:
type LowerCaseString string
func (l *LowerCaseString) UnmarshalJSON(bytes []byte) error {
lowerCasedString := strings.ToLower(string(bytes))
*l = LowerCaseString(lowerCasedString)
return nil
}
Then, in your struct for JSON mapping, you can use your custom type instead of string:
type Person struct {
FirstName LowerCaseString `json:"first_name"`
LastName LowerCaseString `json:"last_name"`
}
If you unmarshal into this struct, the values of FirstName and LastName will be lowercased (also note that you'll need to type convert them back to string to use them as strings).
testJSON := `{"first_name" : "TestFirstNAME", "last_name": "TestLastNAME"}`
var result Person
err := json.Unmarshal([]byte(testJSON), &result)
if err != nil { /*handle the error*/ }
fmt.Println(result.FirstName) // prints "testfirstname"
var stringLastName string
stringLastName = string(result.LastName) // need to type convert from LowerCaseString to string
fmt.Println(stringLastName) // prints "testlastname"
Here is the above code running in Go Playground.

Invalid field name in struct literal Golang

I have this struct call Status and have inside another struct call Data
package model
import "time"
type Status struct {
Data struct {
OrderId string json:"orderId"
DisputeStatus string json:"disputeStatus"
DisputeCauseCategoryName string json:"disputeCauseCategoryName"
ReverseLogisticCost string json:"ReverseLogisticCost"
} json:"data"
StatusJ string json:"status"
}
And i have this function
func (s StatusServices) UpdateStatusItem(ctx context.Context, status model.Status) (error) {
statusDetails := model.Status{
OrderID: status.Data.OrderID,
}
}
In OrderId appear the error Invalid field name, i tried with this
statusDetails := model.Status{
Data: status.Data{
OrderID: status.Data.OrderID,
},
}
but i doesn't work, what can i do
The type model.Status has a field Data with a type defined as
struct {
OrderId string json:"orderId"
DisputeStatus string json:"disputeStatus"
DisputeCauseCategoryName string json:"disputeCauseCategoryName"
ReverseLogisticCost string json:"ReverseLogisticCost"
}
To initialize the variable, you can use a composite literal, more specifically a struct literal
func main() {
s := Status{
Data: struct {
OrderId string `json:"orderId"`
DisputeStatus string `json:"disputeStatus"`
DisputeCauseCategoryName string `json:"disputeCauseCategoryName"`
ReverseLogisticCost string `json:"ReverseLogisticCost"`
}{OrderId: "123456"},
}
fmt.Println(s)
}
https://play.golang.org/p/nLhLHNNf7Jb
I do not believe you can initialize a struct with a nested struct this way.
Best is to give the nested struct its own type. OR you can start with an empty struct (eg var m model.Status) and then (in a separate line) assign to s.Data.OrderId.
I believe you can only assign to a nested struct if you define and initialize it in one go (as is often done in go tests) which is consistent with the idea that this construct is useful only for onetime off uses.
Not sure it answers your question, but you may want to modify your model a little bit from :
type Status struct {
Data struct {
OrderId string json:"orderId"
DisputeStatus string json:"disputeStatus"
DisputeCauseCategoryName string json:"disputeCauseCategoryName"
ReverseLogisticCost string json:"ReverseLogisticCost"
} json:"data"
StatusJ string json:"status"
}
To:
type Status struct {
Data Data `json:"data"`
StatusJ string `json:"status"`
}
type Data struct {
OrderID string `json:"orderId"`
DisputeStatus string `json:"disputeStatus"`
DisputeCauseCategoryName string `json:"disputeCauseCategoryName"`
ReverseLogisticCost string `json:"reverseLogisticCost"`
}
Check the capital letter; in your example you had OrderId in your Data struct but status.Data.OrderID in your function.
Also your field Status does not contain an OrderID field which you were setting in your function.
As mentionned in comment, backticks were missing around your json tags.
A proper IDE would catch those.
Change OrderID to OrderId,declare the field's type upfront as per suggestion of mkopriva:
package main
import (
"fmt"
)
type StatusData struct {
OrderId string `json:"orderId"`
DisputeStatus string `json:"disputeStatus"`
DisputeCauseCategoryName string `json:"disputeCauseCategoryName"`
ReverseLogisticCost string `json:"ReverseLogisticCost"`
}
type Status struct {
Data StatusData
StatusJ string
}
func main() {
s := Status{Data: StatusData{OrderId: "123456"}}
fmt.Println(s)
}
Output:
{{123456 } }

Go unmarshal JSON to struct with nil values [duplicate]

Since empty string is the zero/default value for Go string, I decided to define all such fields as interface{} instead. for example
type student struct {
FirstName interface{} `json:"first_name"`
MiddleName interface{} `json:"middle_name"`
LastName interface{} `json:"last_name"`
}
The application I am sending my data expect a null instead of an empty string if value is not available for that specific field.
Is this the correct approach or can someone please point me to something better than this.
In json package documentation :
Pointer values encode as the value pointed to. A nil pointer encodes as the null JSON object.
So you can store a pointer to a string which will be encoded as a string if not nil and will be encoded as "null" if nil
type student struct {
FirstName *string `json:"first_name"`
MiddleName *string `json:"middle_name"`
LastName *string `json:"last_name"`
}
For the case of a json object with null strings, its easiest to use the omitempty decorator on the field.
type student struct {
FirstName string `json:"first_name,omitempty"`
MiddleName string `json:"middle_name,omitempty"`
LastName string `json:"last_name"`
}
With the above declaration, only if first_name was assigned will that key show up in the resultant json. last_name, on the otherhand, will always show up in the result with a value of "" if not assigned.
Now when you start including numeric fields where 0 could be a value, using omitempty doesn't do what one would expect. A 0 value always drops the field, and we need to be able to differentiate between a 0 value and an unassigned value. Here use of library such as https://github.com/guregu/null may be advisable.
More discussion here: https://www.sohamkamani.com/blog/golang/2018-07-19-golang-omitempty/
Can be used https://github.com/guregu/null
type student struct {
FirstName null.String `json:"first_name"`
MiddleName null.String `json:"middle_name"`
LastName null.String `json:"last_name"`}
Another way actually is a workaround with using the MarhshalJSON and UnmarshalJSON interface method offered by the json lib of golang. The code is as below:
type MyType string
type MyStruct struct {
A MyType `json:"my_type"`
}
func (c MyType) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
if len(string(c)) == 0 {
buf.WriteString(`null`)
} else {
buf.WriteString(`"` + string(c) + `"`) // add double quation mark as json format required
}
return buf.Bytes(), nil
}
func (c *MyType)UnmarshalJSON(in []byte) error {
str := string(in)
if str == `null` {
*c = ""
return nil
}
res := MyType(str)
if len(res) >= 2 {
res = res[1:len(res)-1] // remove the wrapped qutation
}
*c = res
return nil
}
then when using json.Marshal, the MyType value will be marshaled as null.
You could use something like sql.NullString,use 'Valid' to check if it is a nil value.
NullString represents a string that may be null. NullString implements
the Scanner interface so it can be used as a scan destination:
type NullString struct {
String string
Valid bool // Valid is true if String is not NULL
}
var s NullString
err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
...
if s.Valid {
// use s.String
} else {
// NULL value
}
Please DO NOT use pointers like below:
type student struct {
FirstName *string `json:"first_name"`
MiddleName *string `json:"middle_name"`
LastName *string `json:"last_name"`
}
Because you need check nil value and dereference like below everywhere,and maybe cause unexpected crash somewhere:
if obj.FirstName != nil {
fmt.Print("%s", *obj.FirstName)
}
I have compared two solution and choose former method in my extensive production code... it works ok.

Associated belongs with qor admin

I have these two structures:
type Collection struct {
gorm.Model
APIKey string
CollectionID string
Name string
Environments []Environment
}
type Environment struct {
gorm.Model
EnvironmentID string
Name string
Provider string
FlightType string
ADT int
CHD int
INF int
}
And the main looks like:
func main() {
adminResource := admin.New(&admin.AdminConfig{DB: model.DB})
adminResource.AddResource(&model.Collection{})
adminResource.AddResource(&model.Environment{})
mux := http.NewServeMux()
adminResource.MountTo("/admin", mux)
if err := http.ListenAndServe(":8000", mux); err != nil {
panic(err)
} else {
fmt.Println("Listening on: 8000")
}
}
The point is that I donĀ“t know if I should to do something else to allow the membership relationship one-to-many between the Collection and the Environemt. The thing is that the form on the admin view looks good, I can add as many environments as I was... but submitting the form only the Collection are saved on the database.
The thing, which is missing is to tell gorm the foreign key of the other model.
In your case we use Has Many (gorm Doc). To define the relationship you have to add a tag to your struct:
type Collection struct {
gorm.Model
APIKey string
CollectionID string
Name string
Environments []Environment `gorm:"foreignkey:EnvironmentID"`
}
type Environment struct {
gorm.Model
EnvironmentID string
Name string
Provider string
FlightType string
ADT int
CHD int
INF int
}
Without defining the foreign key of the other model gorm is not able to match those both models. As the convention for the primary key is ID and your Enviroment does not have that field it is not possible to match something. Be sure to read the documentation about the conventions.
Finally I found the solution.
type Collection struct {
gorm.Model
APIKey string
CollectionID string
Name string
Environments []Environment
}
type Environment struct {
gorm.Model
EnvironmentID string
Name string
Provider string
FlightType string
ADT int
CHD int
INF int
CollectionID int
}
Adding CollectionID int on the Environment struct is enough... so simply :D.
Thanks #apxp

Assigning null to JSON fields instead of empty strings

Since empty string is the zero/default value for Go string, I decided to define all such fields as interface{} instead. for example
type student struct {
FirstName interface{} `json:"first_name"`
MiddleName interface{} `json:"middle_name"`
LastName interface{} `json:"last_name"`
}
The application I am sending my data expect a null instead of an empty string if value is not available for that specific field.
Is this the correct approach or can someone please point me to something better than this.
In json package documentation :
Pointer values encode as the value pointed to. A nil pointer encodes as the null JSON object.
So you can store a pointer to a string which will be encoded as a string if not nil and will be encoded as "null" if nil
type student struct {
FirstName *string `json:"first_name"`
MiddleName *string `json:"middle_name"`
LastName *string `json:"last_name"`
}
For the case of a json object with null strings, its easiest to use the omitempty decorator on the field.
type student struct {
FirstName string `json:"first_name,omitempty"`
MiddleName string `json:"middle_name,omitempty"`
LastName string `json:"last_name"`
}
With the above declaration, only if first_name was assigned will that key show up in the resultant json. last_name, on the otherhand, will always show up in the result with a value of "" if not assigned.
Now when you start including numeric fields where 0 could be a value, using omitempty doesn't do what one would expect. A 0 value always drops the field, and we need to be able to differentiate between a 0 value and an unassigned value. Here use of library such as https://github.com/guregu/null may be advisable.
More discussion here: https://www.sohamkamani.com/blog/golang/2018-07-19-golang-omitempty/
Can be used https://github.com/guregu/null
type student struct {
FirstName null.String `json:"first_name"`
MiddleName null.String `json:"middle_name"`
LastName null.String `json:"last_name"`}
Another way actually is a workaround with using the MarhshalJSON and UnmarshalJSON interface method offered by the json lib of golang. The code is as below:
type MyType string
type MyStruct struct {
A MyType `json:"my_type"`
}
func (c MyType) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
if len(string(c)) == 0 {
buf.WriteString(`null`)
} else {
buf.WriteString(`"` + string(c) + `"`) // add double quation mark as json format required
}
return buf.Bytes(), nil
}
func (c *MyType)UnmarshalJSON(in []byte) error {
str := string(in)
if str == `null` {
*c = ""
return nil
}
res := MyType(str)
if len(res) >= 2 {
res = res[1:len(res)-1] // remove the wrapped qutation
}
*c = res
return nil
}
then when using json.Marshal, the MyType value will be marshaled as null.
You could use something like sql.NullString,use 'Valid' to check if it is a nil value.
NullString represents a string that may be null. NullString implements
the Scanner interface so it can be used as a scan destination:
type NullString struct {
String string
Valid bool // Valid is true if String is not NULL
}
var s NullString
err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
...
if s.Valid {
// use s.String
} else {
// NULL value
}
Please DO NOT use pointers like below:
type student struct {
FirstName *string `json:"first_name"`
MiddleName *string `json:"middle_name"`
LastName *string `json:"last_name"`
}
Because you need check nil value and dereference like below everywhere,and maybe cause unexpected crash somewhere:
if obj.FirstName != nil {
fmt.Print("%s", *obj.FirstName)
}
I have compared two solution and choose former method in my extensive production code... it works ok.

Resources