This question already has answers here:
Set an int pointer to an int value in Go
(6 answers)
Closed last month.
I need map the struct to create a JSON structure. The collector_id attribute in JSON should be able to take null value or int value.
I hace the follow code:
type purchaseInfo struct {
CollectorID *int64 `json:"collector_id"`
}
func mapPurchaseInfo(collectorID int64) purchaseInfo {
var collectorIDToSend *int64
if collectorID < 0 {
collectorIDToSend = nil
} else {
collectorIDToSend = collectorID
}
return purchaseInfo{
CollectorID: collectorIDToSend,
}
}
This code doesn't compile, the collectorID can't be assigned to collectorIDToSend.
Is there a way to be able to do this?
Thanks!
In the declaration of the mapPurchaseInfo function, to correctly assign the value passed in parameter to collectorIDToSend, the & operator must be used to retrieve the memory address of collectorID.
When constructing the purchaseInfo return variable, it is possible to put the fields of the structure directly as in the example.
type purchaseInfo struct {
CollectorID *int64 `json:"collector_id"`
}
func mapPurchaseInfo(collectorID int64) purchaseInfo {
var collectorIDToSend *int64
if collectorID < 0 {
collectorIDToSend = nil
} else {
collectorIDToSend = &collectorID
}
return purchaseInfo{
CollectorID: collectorIDToSend,
}
}
type purchaseInfo struct {
CollectorID *int64 `json:"collector_id"`
}
func mapPurchaseInfo(collectorID *int64) purchaseInfo {
if *collectorID < 0 {
return purchaseInfo{CollectorID: nil}
}
return purchaseInfo{
CollectorID: collectorID,
}
}
Related
How do i use the following struct to build a nested json recursively?
The nested struct can go to as many levels as it can.
Both the sample struct and json is mentioned below.
I'm having trouble in building a nested json/object dynamically.
I used the reflect package to access the struct.
I'm able to read through the data but not able to build the same.
type Data struct {
ID string
Name string
Types *Details
}
type Details struct {
Customer int32
Countries int32
}
To:
{
"Name":"Data",
"Fields":[{
"Name":"ID",
"Type":"string"
},
{
"Name":"Name",
"Type":"string"
},
{
"Name":"Details",
"Type":"struct",
"Fields":[{
"Name":"Customer",
"Type":"int32"
},{
"Name":"Countries",
"Type":"int32"
}]
}
And whatever I have done so far, I have attached below:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func getFields(t reflect.Type, prefix string) {
switch t.Kind() {
case reflect.Ptr:
getFields(t.Elem(), "")
case reflect.Struct:
buildRecursiveFunction(t)
}
}
func buildRecursiveFunction(t reflect.Type) map[string]interface{} {
var jsonArr []interface{}
temp := make(map[string]interface{})
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Name != "state" && sf.Name != "unknownFields" && sf.Name != "sizeCache" {
obj := make(map[string]interface{})
varName := sf.Name
varType := sf.Type.Kind()
obj["Name"] = varName
obj["Type"] = varType.String()
if varType.String() == "ptr" {
obj["Fields"] = buildRecursiveFunction(sf.Type.Elem())
} else {
}
jsonArr = append(jsonArr, obj)
}
}
jsonArrVal, _ := json.Marshal(jsonArr)
fmt.Println(string(jsonArrVal))
return temp
}
func main() {
getFields(reflect.TypeOf(&DeviceEnv{}), "")
// json.Marshal(AllAttributes)
}
Any help is appreciated.
I've hacked around your code and got it (kind of) working here, but it doesn't seem like it's structured in the best way. Better to return and build a recursive object than trying to print it (with a prefix?) as you go.
What about defining a TypeInfo struct and having a recursive function that populates that? I think that leads to a clear structure, and it allows the caller to JSON-marshal the result as they want to:
func main() {
info := MakeTypeInfo("DeviceEnvironment", DeviceEnv{})
b, err := json.MarshalIndent(info, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", b)
}
type TypeInfo struct {
Name string `json:"Name"`
Type string `json:"Type"`
Fields []TypeInfo `json:"Fields,omitempty"`
}
func MakeTypeInfo(name string, value interface{}) TypeInfo {
return makeTypeInfo(name, reflect.TypeOf(value))
}
func makeTypeInfo(name string, t reflect.Type) TypeInfo {
kind := t.Kind()
switch kind {
case reflect.Struct:
var fields []TypeInfo
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fields = append(fields, makeTypeInfo(field.Name, field.Type))
}
return TypeInfo{Name: name, Type: kind.String(), Fields: fields}
case reflect.Pointer:
return makeTypeInfo(name, t.Elem())
default:
return TypeInfo{Name: name, Type: kind.String()}
}
}
Note that I haven't done the field filtering (eg: "unknownFields") that you've shown in your code -- shouldn't be hard to add inside the reflect.Struct case though.
Full example in Go Playground.
what I try to do
I try to pass an instance of a struct - including json tags to a func, create a new instance, and set value on field
after this i try to serialize (JSON), but the values are empty
NOTICE: i looked up loads of articles on SO about setting values via reflection, but it seems i missed a little detail
struct definition
this part defines the struct with json and xml tags
type Person struct {
Name string `json:"Name" xml:"Person>FullName"`
Age int `json:"Age" xml:"Person>Age"`
}
create instance (+wrapping into empty interface)
afterwards I create an instance and store it in an interface{} - why? because in my production code this stuff will be done in a func which accepts a interface{}
var iFace interface{} = Person{
Name: "Test",
Age: 666,
}
creating a new instance of the struct and setting values via reflection
iFaceType := reflect.TypeOf(iFace)
item := reflect.New(iFaceType)
s := item.Elem()
if s.Kind() == reflect.Struct {
fName := s.FieldByName("Name")
if fName.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if fName.CanSet() {
// change value of N
switch fName.Kind() {
case reflect.String:
fName.SetString("reflectedNameValue")
fmt.Println("Name was set to reflectedNameValue")
}
}
}
fAge := s.FieldByName("Age")
if fAge.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if fAge.CanSet() {
// change value of N
switch fAge.Kind() {
case reflect.Int:
x := int64(42)
if !fAge.OverflowInt(x) {
fAge.SetInt(x)
fmt.Println("Age was set to", x)
}
}
}
}
}
Question
what am I doing wrong?
in production code I fill multiple copies with data and add it to a slice...
but this only makes sense if the json tags are kept in place and the stuff serializes just the same way.
code sample for play
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
type Person struct {
Name string `json:"Name" xml:"Person>FullName"`
Age int `json:"Age" xml:"Person>Age"`
}
var iFace interface{} = Person{
Name: "Test",
Age: 666,
}
fmt.Println("normal: \n" + JSONify(iFace))
iFaceType := reflect.TypeOf(iFace)
item := reflect.New(iFaceType)
s := item.Elem()
if s.Kind() == reflect.Struct {
fName := s.FieldByName("Name")
if fName.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if fName.CanSet() {
// change value of N
switch fName.Kind() {
case reflect.String:
fName.SetString("reflectedNameValue")
fmt.Println("Name was set to reflectedNameValue")
}
}
}
fAge := s.FieldByName("Age")
if fAge.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if fAge.CanSet() {
// change value of N
switch fAge.Kind() {
case reflect.Int:
x := int64(42)
if !fAge.OverflowInt(x) {
fAge.SetInt(x)
fmt.Println("Age was set to", x)
}
}
}
}
}
fmt.Println("reflected: \n" + JSONify(item))
}
func JSONify(v interface{}) string {
var bytes []byte
bytes, _ = json.MarshalIndent(v, "", "\t")
return string(bytes)
}
Your item is of type reflect.Value. You have to call Value.Interface() to obtain the value wrapped in it:
fmt.Println("reflected: \n" + JSONify(item.Interface()))
With this change, output will be (try it on the Go Playground):
normal:
{
"Name": "Test",
"Age": 666
}
Name was set to reflectedNameValue
Age was set to 42
reflected:
{
"Name": "reflectedNameValue",
"Age": 42
}
reflect.Value itself is also a struct, but obviously trying to marshal it will not be identical to marshaling a Person struct value. reflect.Value does not implement marshaling the wrapped data to JSON.
I have a struct in Go that came from and XML resp body:
{
"pdp":{
"sellableUnits":[
{
"attributes":[
{
"id":"22555278",
"type":"size",
"value":"03.5"
}
]
}
]
}
}
type sizeJ struct {
PDP struct {
SellableUnits []struct {
Attributes []struct {
ID string `json:"id"`
Type string `json:"type"`
Val string `json:"value"`
} `json:"attributes"`
} `json:"units"`
} `json:"pdp"`
}
There are different Vals and a different ID depending on the value of Val.
Use a for loop, with range if you like.
func getValByID(j sizeJ, id string) string {
for _, u := range j.PDP.SellableUnits {
for _, a := range u.Attributes {
if a.ID == id {
return a.Val
}
}
}
return ""
}
func getIDByVal(j sizeJ, val string) string {
for _, u := range j.PDP.SellableUnits {
for _, a := range u.Attributes {
if a.Val == val {
return a.ID
}
}
}
return ""
}
https://play.golang.org/p/LjPrs1yGKGc
you can use map instead of nested struct,such as
var myMap map[string]map[string]interface{}
or
var myMap map[string]interface{}
or every thing you want.
I need to show json's empty object {} when do json.Marshal() for a struct pointer. I can only output either null value or empty struct value.
If the person key is filled with &Person{} or new(Person), it will show empty struct like below:
{
"data": {
"person": {
"name": "",
"age": 0
},
"created_date": "2009-11-10T23:00:00Z"
}
}
And if we don't initialize it at all, it will show null.
{
"data": {
"person": null,
"created_date": "2009-11-10T23:00:00Z"
}
}
I want to show "person": {}. Is it possible?
Go Playground for the complete code: https://play.golang.org/p/tT15G2ESPVc
Option A, use the omitempty tag option on all of the Person's fields and make sure the response's field is allocated before marshaling.
type Person struct {
Name string `json:"name,omitempty"`
Age int `json:"age,omitempty"`
}
// ...
resp.Person = new(Person)
https://play.golang.org/p/o3jWdru_8bC
Option B, use a non-pointer wrapper type that embeds the Person pointer type.
type PersonJSON struct {
*Person
}
type Response struct {
Person PersonJSON `json:"person"`
CreatedDate time.Time `json:"created_date"`
}
https://play.golang.org/p/EKQc7uf1_Vk
Option C, have the Reponse type implement the json.Marshaler interface.
func (r *Response) MarshalJSON() ([]byte, error) {
type tmp Response
resp := (*tmp)(r)
var data struct {
Wrapper struct {
*Person
} `json:"person"`
*tmp
}
data.Wrapper.Person = resp.Person
data.tmp = resp
return json.Marshal(data)
}
https://play.golang.org/p/1qkSCWZ225j
There may be other options...
In Go, an empty struct by definition assigns zero values to field elements. Eg: for int 0, "" for string, etc.
For your case, simply comparing to null would work out. Or, you could define an emptyPerson as:
var BAD_AGE = -1
emptyPerson := &Person{"", BAD_AGE} // BAD_AGE indicates no person
if person[age] == BAD_AGE {
// handle case for emptyPerson}
I have one struct which is getting used with protobuff serialiser and works well.
This struct is generated by protobuff hence it has so many methods like Unmarshal etc.
type Flight struct {
FlightNo string `json:"flightno, omitempty"`
Carrier string `json:"carrier, omitempty"`
}
func (m *Flight) Unmarshal(data []byte) error {
l := len(data)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowFlight
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
}
// some more code
}
Then I want to add additional field to this flight info,
type FlightMeta struct {
Source string `json:"source, omitempty"`
Destination string `json:"destination, omitempty"`
}
Then i have combined struct of these two,
type CombinedFlight struct {
Flight
FlightMeta
}
type ResponseFlight struct {
OnwardFlights []CombinedFlight `json:"onwardflights, omitempty"`
ReturnFlights []CombinedFlight `json:"returnflights, omitempty"`
Error string `json:"error, omitempty"`
}
So when i read some data like,
str "= `{"onwardflights": [{"flightno": "537", "carrier": "AI", "source": "BOM", "destination": "DEL"}], "error": "false"}`
respFlight = new(ResponseFlight)
err = json.Unmarshal([]byte(str), &respFlight)
fmt.Println("flightno:", respFlight.OnwardFlights[0].FlightNo, "flight source", respFlight.OnwardFlights[0].Source)
#prints "flightno:537 flight source:
It doesn't print value for second struct, as per methis is not unmarshling properly.
But when i set the attribute manually and marshal(encode it to json) this is working fine.
Any leads.?
Try to do this
type CombinedFlight1 struct {
Flight
FlightMeta
}
type CombinedFlight2 struct {
Flight
FlightMeta
}
type ResponseFlight struct {
OnwardFlights []CombinedFlight1 `json:"onwardflights, omitempty"`
ReturnFlights []CombinedFlight2 `json:"returnflights, omitempty"`
Error string `json:"error, omitempty"`
}
It will work fine and if you want to marshal, you have to create two different structure. For more understanding see this post
Visit Initialize nested struct definition in Golang if it have same objects