Convert string to custom struct Go - go

Hi I have written code to read data from MongoDB.
It worked but I don't know how to convert string result to my custom struct.
Here is my code
type UserSSO struct {
Username string `json:"username"`
Password string `json:"password"`
Lastname string `json:"lastname"`
Useremail string `json:"useremail"`
Usertel string `json:"usertel"`
Userdate string `json:"userdate"`
Userstatus string `json:"userstatus"`
Userparentid string `json:"userparentid"`
Comid string `json:"comid"`
Comdepartment string `json:"comdepartment"`
Usercode string `json:"usercode"`
Usertype string `json:"usertype"`
}
func GetInfomationChildOfNode(node string) (err error, info string) {
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://casuser:Mellon#222.255.102.145:27017/users"))
if err != nil {
return err, ""
}
defer client.Disconnect(ctx)
database := client.Database("users")
users := database.Collection("users")
matchStage := bson.D{{"$match", bson.D{{"username", node}}}}
graphStage := bson.D{{"$graphLookup", bson.D{{"from", "users"}, {"startWith", "$username"}, {"connectFromField", "username"}, {"connectToField", "userparentid"}, {"as", "descendants"}}}}
unWind := bson.D{{"$unwind", "$descendants"}}
replaceRoot := bson.D{{"$replaceRoot", bson.D{{"newRoot", "$descendants"}}}}
proJect := bson.D{{"$project", bson.D{{"descendants", 0}}}}
showInfoCursor, err := users.Aggregate(ctx, mongo.Pipeline{matchStage, graphStage, unWind, replaceRoot, proJect})
if err != nil {
return err, ""
}
var showsWithInfo []bson.M
if err = showInfoCursor.All(ctx, &showsWithInfo); err != nil {
return err, ""
}
data, _ := json.Marshal(showsWithInfo)
stringData := string(data)
fmt.Println(stringData)
return nil, stringData
}
And here is my string result output
[{"_id":"5ee0ac96653a000065005c03","comdepartment":"KHOA_DIEN","comid":"DHBK","lastname":"KHOA_DIEN","password":"123456","usercode":"DHBK_0002","userdate":"2020-05-05","useremail":"KHOA_DIEN#edu.com.vn","username":"KHOA_DIEN","userparentid":"DHBK","userstatus":"ACTIVE","usertel":"0907111002","usertype":"USER_COM"},{"_id":"5ee0ac96653a000065005c04","comdepartment":"KHOA_XD","comid":"DHBK","lastname":"KHOA_XD","password":"123456","usercode":"DHBK_0003","userdate":"2020-05-05","useremail":"KHOA_XD#edu.com.vn","username":"KHOA_XD","userparentid":"DHBK","userstatus":"DISABLE","usertel":"0907111003","usertype":"USER_COM"},{"_id":"5ee0ac96653a000065005c08","comdepartment":"KHOA_DIEN","comid":"DHBK","lastname":"BOMON_HETHONG","password":"123456","usercode":"DHBK_0007","userdate":"2020-05-05","useremail":"BOMON_HETHONG#edu.com.vn","username":"BOMON_HETHONG","userparentid":"KHOA_DIEN","userstatus":"ACTIVE","usertel":"0907111007","usertype":"USER_COM"},{"_id":"5ee0ac96653a000065005c09","comdepartment":"KHOA_XD","comid":"DHBK","lastname":"BOMON1_XD","password":"123456","usercode":"DHBK_0008","userdate":"2020-05-05","useremail":"BOMON1_XD#edu.com.vn","username":"BOMON1_XD","userparentid":"KHOA_XD","userstatus":"DISABLE","usertel":"0907111008","usertype":"USER_COM"},{"_id":"5ee0ac96653a000065005c0a","comdepartment":"KHOA_XD","comid":"DHBK","lastname":"BOMON2_XD","password":"123456","usercode":"DHBK_0009","userdate":"2020-05-05","useremail":"BOMON2_XD#edu.com.vn","username":"BOMON2_XD","userparentid":"KHOA_XD","userstatus":"DISABLE","usertel":"0907111009","usertype":"USER_COM"},{"_id":"5ee0ac96653a000065005c0b","comdepartment":"KHOA_XD","comid":"DHBK","lastname":"BOMON3_XD","password":"123456","usercode":"DHBK_0010","userdate":"2020-05-05","useremail":"BOMON3_XD#edu.com.vn","username":"BOMON3_XD","userparentid":"KHOA_XD","userstatus":"DISABLE","usertel":"0907111010","usertype":"USER_COM"},{"_id":"5ee0ac96653a000065005c05","comdepartment":"KHOA_CNTT","comid":"DHBK","lastname":"KHOA_CNTT","password":"123456","usercode":"DHBK_0004","userdate":"2020-05-05","useremail":"KHOA_CNTT#edu.com.vn","username":"KHOA_CNTT","userparentid":"DHBK","userstatus":"ACTIVE","usertel":"0907111004","usertype":"USER_COM"},{"_id":"5ee0ac96653a000065005c06","comdepartment":"KHOA_DIEN","comid":"DHBK","lastname":"BOMON_TUDONG","password":"123456","usercode":"DHBK_0005","userdate":"2020-05-05","useremail":"BOMON_TUDONG#edu.com.vn","username":"BOMON_TUDONG","userparentid":"KHOA_DIEN","userstatus":"ACTIVE","usertel":"0907111005","usertype":"USER_COM"}]
In addition, I used this code to convert bson.M to struct, but it fail
var userSSO UserSSO
bsonBytes, _ := bson.Marshal(showsWithInfo)
bson.Unmarshal(bsonBytes, &userSSO)
fmt.Println(userSSO)
Result is { }
Thank you in advance.

var userSSO UserSSO
bsonBytes, _ := bson.Marshal(showsWithInfo)
bson.Unmarshal(bsonBytes, &userSSO)
fmt.Println(userSSO)
You are marshalling with bson and trying to unmarshal with json. Both are different formats and hence this will not work.
You can do something like this below to unmarshal a map[string]interface{} (which is bascically bson.M) into a struct
package main
import (
"encoding/json"
"fmt"
"go.mongodb.org/mongo-driver/bson"
)
type data struct {
StringField string `json:"stringField"`
IntField int `json:"intField,string"`
}
func main() {
i := []bson.M{
{
"stringField": "foo1",
"intField": "123",
},
{
"stringField": "foo2",
"intField": "456",
},
}
bs, err := json.Marshal(i)
if err != nil {
panic(err)
}
var o []data
if err := json.Unmarshal(bs, &o); err != nil {
panic(err)
}
fmt.Println("Out: ", o)
}
Notice the intField,string. You can read more about this here

Related

Is there an easy way to create a struct in golang?

I have a struct and now want to instantiate it from received http data. But now the code I write is cumbersome and has a lot of lines of code. Is there any way to simplify the code? All fields except the field id can correspond
model
type ALiNotifyLog struct {
ID *int `json:"id"`
APPId *string `json:"app_id"`
AuthAppId *string `json:"auth_app_id"`
BuyerId *string `json:"buyer_id"`
BuyerPayAmount *string `json:"buyer_pay_amount"`
GmtCreate *string `json:"gmt_create"`
GmtPayment *string `json:"gmt_payment"`
InvoiceAmount *string `json:"invoice_amount"`
NotifyId *string `json:"notify_id"`
NotifyTime *string `json:"notify_time"`
OutTradeNo *string `json:"out_trade_no"`
PointAmount *string `json:"point_amount"`
ReceiptAmount *string `json:"receipt_amount"`
Sign *string `json:"sign"`
TotalAmount *string `json:"total_amount"`
TradeNo *string `json:"trade_no"`
TradeStatus *string `json:"trade_status"`
}
func
func SaveData(data map[string]interface{}) {
app_id := data["app_id"].(string)
auth_app_id := data["auth_app_id"].(string)
buyer_id := data["buyer_id"].(string)
buyer_pay_amount := data["buyer_pay_amount"].(string)
gmt_create := data["gmt_create"].(string)
gmt_payment := data["gmt_payment"].(string)
invoice_amount := data["invoice_amount"].(string)
notify_id := data["notify_id"].(string)
notify_time := data["notify_time"].(string)
out_trade_no := data["out_trade_no"].(string)
point_amount := data["point_amount"].(string)
receipt_amount := data["receipt_amount"].(string)
sign := data["sign"].(string)
total_amount := data["total_amount"].(string)
trade_no := data["trade_no"].(string)
trade_status := data["trade_status"].(string)
model := payment.ALiNotifyLog{
APPId: &app_id,
AuthAppId: &auth_app_id,
BuyerId: &buyer_id,
BuyerPayAmount: &buyer_pay_amount,
GmtCreate: &gmt_create,
GmtPayment: &gmt_payment,
InvoiceAmount: &invoice_amount,
NotifyId: &notify_id,
NotifyTime: &notify_time,
OutTradeNo: &out_trade_no,
PointAmount: &point_amount,
ReceiptAmount: &receipt_amount,
Sign: &sign,
TotalAmount: &total_amount,
TradeNo: &trade_no,
TradeStatus: &trade_status}
res := global.Orm.Table(paynotifylog).Create(&model)
fmt.Println(res)
}
I see that you are JSON. May be decode the JSON directly into a struct instance.
I will structure the code something like the below snippet:
type ALiNotifyLog struct {
// your fields here
}
func parseRequest(r *http.Request) {
var notifyLog ALiNotifyLog
err := json.NewDecoder(r.Body).Decode(&notifyLog)
if err != nil {
// do something
}
// ............ more code
}
func SaveData(data ALiNotifyLog) {
res := global.Orm.Table(paynotifylog).Create(&data)
fmt.Println(res)
// ........... more code
}
Use the reflect package to programmatically iterate over the fields:
func setStringpFields(pmodel any, data map[string]any) {
v := reflect.ValueOf(pmodel).Elem()
t := v.Type()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
if sf.Type != stringpType {
continue
}
name, _, _ := strings.Cut(sf.Tag.Get("json"), ",")
if s, ok := data[name].(string); ok {
v.Field(i).Set(reflect.ValueOf(&s))
}
}
}
var stringpType = reflect.PtrTo(reflect.TypeOf(""))
Use it like this:
var model ALiNotifyLog
setStringpFields(&model, data)
Run an example on the Go Playground.
I took the liberty of skipping fields that are missing from data. The code in the question panics on a missing value.
A simpler approach is to create a function with the repeated functionality:
func stringp(data map[string]interface{}, name string) *string {
if s, ok := data[name].(string); ok {
return &s
}
return nil
}
Use that function to initialize the fields:
model := payment.ALiNotifyLog{
APPId: stringp("app_id"),
AuthAppId: stringp("auth_app_id"),
...
TradeStatus: stringp("trade_status")}
res := global.Orm.Table(paynotifylog).Create(&model)
fmt.Println(res)
1: ScanMapToStruct
func scanMapToStruct(dest interface{}, vals map[string]interface{}) error {
srcValue := indirect(reflect.ValueOf(dest))
srcType := indirectType(reflect.TypeOf(dest))
for m := 0; m < srcType.NumField(); m++ {
field := srcType.Field(m)
fieldName, _ := getFieldName(field)
jsonTypeName := getJsonDataType(field.Type)
if jsonTypeName == "" {
continue
}
v, ok := vals[fieldName].(string)
if !ok {
continue
}
dec := decoders[field.Type.Kind()]
if dec == nil {
continue
}
err := dec(srcValue.Field(m), v)
if err != nil {
fmt.Printf("set field(%s)=%s err:%v\n", fieldName, v, err)
continue
}
}
return nil
}
2: copier.Copy()
Use package mapstructure from GitHub.
go get https://github.com/mitchellh/mapstructure
package main
import (
"log"
"os"
"github.com/mitchellh/mapstructure"
)
type MyStruct struct {
This int
That string `json:"thaaaaat"`
}
func main() {
var result map[string]interface{}
cfg := &mapstructure.DecoderConfig{
TagName: "json",
Result: &result,
}
decoder, err := mapstructure.NewDecoder(cfg)
if err != nil {
log.Printf("Could not create decoder: %v", err)
os.Exit(1)
}
myData := &MyStruct{
This: 42,
That: "foobar",
}
err = decoder.Decode(myData)
if err != nil {
log.Printf("Decoding failed: %v", err)
os.Exit(1)
}
log.Print(cfg.Result)
}
Output:
&map[This:42 thaaaaat:foobar]
https://go.dev/play/p/mPK_9fEevyC

String to float64 receiving format ".01"

If I receive from an API a string obeying the format of ".01", and I have a struct like this:
type Mystruct struct {
Val float64 json:"val,string"
}
In this case, I receive trying to unmarshal val into float64. Is there a way I can accomplish this?
Add a string field to capture the string value:
type Mystruct struct {
Val float64 `json:"-"`
XVal string `json:"val"`
}
Unmarshal the JSON document. Convert the string value to a float value:
var v Mystruct
err := json.Unmarshal([]byte(data), &v)
if err != nil {
log.Fatal(err)
}
v.Val, err = strconv.ParseFloat(v.XVal, 64)
if err != nil {
log.Fatal(err)
}
I recommand defining a type alias which you can use it anywhere.
package main
import (
"encoding/json"
"fmt"
"strconv"
"strings"
)
type MyFloat64 float64
func (f *MyFloat64) UnmarshalJSON(data []byte) error {
raw := string(data)
raw = strings.TrimPrefix(raw, "\"")
raw = strings.TrimSuffix(raw, "\"")
if parsedFloat, err := strconv.ParseFloat(raw, 64); err != nil {
return err
} else {
*f = MyFloat64(parsedFloat)
return nil
}
}
type MyObj struct {
Val1 MyFloat64
Val2 string
}
func main() {
j := `{"Val1":"0.01", "Val2":"0.01"}`
o := MyObj{}
err := json.Unmarshal([]byte(j), &o)
if err != nil {
fmt.Println(err)
} else {
b, _ := json.Marshal(o)
fmt.Println("in:", j)
fmt.Println("out:", string(b))
}
}
output:
in: {"Val1":"0.01", "Val2":"0.01"}
out: {"Val1":0.01,"Val2":"0.01"}

How to access map[string]interface {}?

How can i programmatically access the msg value "Design" in the go language structure shown below?
after subIssues[28].Fields.Unknowns["customfield_11801"] i dont find a language construct to access the data structure.
To convert into a struct and work with that, have a look at this repo: https://github.com/mitchellh/mapstructure.
And if you want to do it your self, something like this: (Adjust implementation to match your project)
func SetField(obj interface{}, name string, value interface{}) error {
structValue := reflect.ValueOf(obj).Elem()
structFieldValue := structValue.FieldByName(name)
if !structFieldValue.IsValid() {
return fmt.Errorf("No such field: %s in obj", name)
}
if !structFieldValue.CanSet() {
return fmt.Errorf("Cannot set %s field value", name)
}
structFieldType := structFieldValue.Type()
val := reflect.ValueOf(value)
if structFieldType != val.Type() {
return errors.New("Provided value type didn't match obj field type")
}
structFieldValue.Set(val)
return nil
}
type MyStruct struct {
Name string
Age int64
}
func (s *MyStruct) FillStruct(m map[string]interface{}) error {
for k, v := range m {
err := SetField(s, k, v)
if err != nil {
return err
}
}
return nil
}
func main() {
myData := make(map[string]interface{})
myData["Name"] = "Tony"
myData["Age"] = int64(23)
result := &MyStruct{}
err := result.FillStruct(myData)
if err != nil {
fmt.Println(err)
}
fmt.Println(result)
}

Golang Unmarshal an JSON response, then marshal with Struct field names

So I am hitting an API that returns a JSON response and I am unmarshalling it into a struct like so:
package main
type ProcessedRecords struct {
SLMIndividualID string `json:"individual_id"`
HouseholdPosition int `json:"Household Position"`
IndividualFirstName string `json:"individual_first_name"`
}
func main() {
req, _ := http.NewRequest(method, url, payload)
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
}
fmt.Println(body)
var responseObject Response
json.Unmarshal(body, &responseObject)
fmt.Println(responseObject)
which works great. However I need to marshal this struct again but I want to use the Struct Fields as keys instead of the json: ... fields. I am using the following code:
recordsInput := []*firehose.Record{}
for i := 0; i < len(records); i++ {
if len(recordsInput) == 500 {
* code to submit records, this part works fine *
}
b, err := json.Marshal(records[i])
if err != nil {
log.Printf("Error: %v", err)
}
record := &firehose.Record{Data: b}
recordsInput = append(recordsInput, record)
}
This does submit records successfully but it's in the format:
{"individual_id":"33c05b49-149b-480f-b1c2-3a3b30e0cb6f","Household Position":1...}
and I'd like it in the format:
{"SLMIndividualId":"33c05b49-149b-480f-b1c2-3a3b30e0cb6f","HouseholdPosition":1...}
How can I achieve this?
Those tags say how the struct should be marshalled, so if they are present, that is how the output will be. You'll need to convert it to a matching struct that does not have the json: tags:
type ProcessedRecords struct {
SLMIndividualID string `json:"individual_id"`
HouseholdPosition int `json:"Household Position"`
IndividualFirstName string `json:"individual_first_name"`
}
type ProcessedRecordsOut struct {
SLMIndividualID string
HouseholdPosition int
IndividualFirstName string
}
func process() {
var in ProcessedRecords
json.Unmarshal(data, &in)
// Convert to same type w/o tags
out := ProcessedRecordsOut(in)
payload, _ := json.Marshal(out)
// ...
}
See a working example here: https://play.golang.org/p/p0Fc8DJotYE
You can omit fields one-way by defining a custom type and implementing the correct interface, e.g.
package main
import (
"encoding/json"
"fmt"
)
type Animal struct {
Name ReadOnlyString
Order string
}
type ReadOnlyString string
func (ReadOnlyString) UnmarshalJSON([]byte) error { return nil }
func main() {
x := Animal{"Bob", "First"}
js, err := json.Marshal(&x)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%s\n", js)
var jsonBlob = []byte(`{"Name": "Platypus", "Order": "Monotremata"}`)
if err := json.Unmarshal(jsonBlob, &x); err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%#v\n\n", x)
}
https://go.dev/play/p/-mwBL0kIqM
Found this answer here: https://github.com/golang/go/issues/19423#issuecomment-284607677

Read flattened entity from cloud datastore in golang

func (db *dataStore) AddAcceptance(ctx context.Context, req *acceptance.PolicyAcceptance) (uint64, error) {
accpKey := datastore.IncompleteKey("Acceptance", nil)
key, err := db.Put(context.Background(), accpKey, req);
if err != nil {
log.Fatalf("Failed to save Acceptance: %v", err)
}
accpKey = key
val := uint64(accpKey.ID)
return val, err
}
type PolicyAcceptance struct {
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
PolicyNumber int64 `protobuf:"varint,2,opt,name=policyNumber" json:"policyNumber,omitempty"`
Version string `protobuf:"bytes,3,opt,name=version" json:"version,omitempty"`
SignerData *SignerData `protobuf:"bytes,4,opt,name=signerData" json:"signerData,omitempty" datastore:",flatten"`
GroupID int64 `protobuf:"varint,5,opt,name=groupID" json:"groupID,omitempty"`
LocationID int64 `protobuf:"varint,6,opt,name=locationID" json:"locationID,omitempty"`
BusinessId int64 `protobuf:"varint,7,opt,name=businessId" json:"businessId,omitempty"`
AcceptedDate *google_protobuf.Timestamp `protobuf:"bytes,8,opt,name=acceptedDate" json:"acceptedDate,omitempty" datastore:",flatten"`
IssuerName string `protobuf:"bytes,9,opt,name=issuerName" json:"issuerName,omitempty"`
Place string `protobuf:"bytes,10,opt,name=place" json:"place,omitempty"`
}
type SignerData struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email" json:"email,omitempty"`
Type string `protobuf:"bytes,3,opt,name=type" json:"type,omitempty"`
Id int64 `protobuf:"varint,4,opt,name=id" json:"id,omitempty"`
}
datastore:",flatten" saves data as flattened in data store. The property names becomes flattened with . like SignerData.Id as property name but when it's read from data store, how can I map it back to struct? It fails throwing an error like:
SignerData.Id could not be found as a key in struct. Error: No such
struct field.
func (db *dataStore) GetAcceptanceBySignerData(ctx context.Context, req *acceptance.SignerData) (*acceptance.ListOfPolicyAcceptance, error) {
query := datastore.NewQuery("Acceptance").Filter("SignerData.Id =", req.Id)
var accpArr acceptance.ListOfPolicyAcceptance
var err error
it := db.Run(ctx, query)
for {
var accept acceptance.PolicyAcceptance
_, err := it.Next(&accept)
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("Error fetching : %v", err)
}
accpArr.AcceptanceList = append(accpArr.AcceptanceList, &accept)
}
return &accpArr, err
}

Resources