Golang RethinkDB ChangeFeed Structure - go

I was wondering if someone could explain how to unmarshal my changefeed cursor value to a specific struct type.
var message map[string]interface{}
for chatFeedCursor.Next(&message) {
fmt.Println(message)
}
map[new_val:map[club_id:ea2eb6e2-755f-4dad-922d-e3693b6e55c6
date:2017-04-07 14:48:17.714 +0100 +01:00
id:e389ab54-963e-4b33-9b34-adcb6ec5b17e message:what is the meaning of life?
user_id:00ff679f-9421-4b8b-ae7f-d11cf2adaee2] old_val:]
However, I would like the response to be mapped to struct ChatMessage.
Update:
I've tried:
var message ChatMessage
However, it doesn't seem like any of my data gets set in the struct.
{ 0001-01-01 00:00:00 +0000 UTC}
My struct:
type ChatMessage struct {
ID string `json:"id" gorethink:"id,omitempty"`
UserID string `json:"user_id" gorethink:"user_id"`
ClubID string `json:"club_id" gorethink:"club_id"`
Message string `json:"message" gorethink:"message"`
Date time.Time `json:"date" gorethink:"date"`
}
Thanks.

I figured it out!
The problem was that I didn't specify a field on the rethinkdb change request.
Previous code:
chatFeedCursor, _ := gorethink.Table("club_chat").Changes().Run(gorethinkSession)
Working Code:
chatFeedCursor, _ := gorethink.Table("club_chat").Changes().Field("new_val").Run(gorethinkSession)
Now the .Next() value maps to my struct with no issues.

If you want to unmarshal the changefeed cursor value to a specific struct type, but keep both the new and the old value, you could use a library for decoding generic map values into native Go structures, like mapstructure, to obtain a neat result:
Import mapstructure:
import (
...
"github.com/mitchellh/mapstructure"
...
)
Now you can do something like:
cursor, err := r.Table("club_chat").
Changes().
Run(db.Session)
if err != nil {
// manage error
}
var changeFeed map[string]interface{}
for cursor.Next(&changeFeed) {
var oldVal, newVal ChatMessage
if changeFeed["old_val"] != nil {
mapstructure.Decode(changeFeed["old_val"], &oldVal)
}
if changeFeed["new_val"] != nil {
mapstructure.Decode(changeFeed["new_val"], &newVal)
}
// Do what you want with the values

Related

Json unmarshal struct to map is ignoring zero values of struct

I am trying to convert a struct to map using following method
func ConvertStructToMap(in interface{}) map[string]interface{} {
fmt.Println(in)
var inInterface map[string]interface{}
inrec, _ := json.Marshal(in)
json.Unmarshal(inrec, &inInterface)
return inInterface
}
The problem is when I am trying to convert a struct
type Data struct{
Thing string `json:"thing,omitempty"`
Age uint8 `json:"age,omitempty"`
}
With this data
c:=Data{
Thing :"i",
Age:0,
}
it just gives me the following output map[things:i] instead it should give the output
map[things:i,age:0]
And when I don't supply age
like below
c:=Data{
Thing :"i",
}
Then it should give this output map[things:i] .I am running an update query and the user may or may not supply the fields ,any way to solve it .I have looked over the internet but couldn't get my head on place
Edit -
I am sending json from frontend which gets converted to struct with go fiber Body parser
if err := c.BodyParser(&payload); err != nil {
}
Now If I send following payload from frontend
{
thing:"ii"
}
the bodyParser converts it into
Data{
Thing :"ii",
Age :0
}
that's why I have used omitempty such that bodyParser can ignore the field which are not present
omitempty ignore when convert from struct to json
type Response struct {
Age int `json:"age,omitempty"`
Name string `json:"name,omitempty"`
}
resp:=Response {
Name: "john",
}
data,_:=json.Marshal(&resp)
fmt.Println(string(data)) => {"name": "john"}
In you case, you convert from json to struct, if attribute not present, it will be fill with default value of data type, in this case is 0 with uint8

How to write struct onto ledger state

I am trying to write chaincode for Hyperledger that has a mapping, that stores struct values mapped to strings. This is my first time writing contracts for Hyperledger and also my first time using go and it appears I am not approaching this the right way.
This is my mapping, Data struct, Init function and addVData function that shows the problem.
type Data struct{
Timestamp string
Velocity string
Location string
}
var all_data map[string]Data
func (t *DataContract) Init(stub shim.ChaincodeStubInterface) peer.Response {
all_data = make(map[string]Data)
return shim.Success(nil)
}
func (t *DataContract) addVData(stub shim.ChaincodeStubInterface, args []string) peer.Response {
params := stub.GetStringArgs()
fmt.Println("The params passed in are:", params)
if len(params) != 4 {
fmt.Println("Please resubmit in this particular order: addVData, Timestamp, Velocity, Location")
return shim.Error("Please resubmit in this particular order: addVData, Timestamp, Velocity, Location")
}
var d = Data{Timestamp:params[1], Velocity:params[2], Location:params[3]}
all_data[params[1]] = d
var err = stub.PutState(params[1],d)
return shim.Success(nil)
}
The error I am getting is actually very clear:
./data.go:79:35: cannot use d (type Data) as type []byte in argument to stub.PutState
I am wondering, since my data is not in form of a byte array, how do I go about storing it?
Also, I am not certain I have implemented the Init method and the mappings in the correct way but have had a hard time finding examples. If you could please explain and point me in the right direction it would be very appreciated, thank you.
Use json.Marshal function to convert the struct into bytes
type UserData struct {
a string
}
userdata := &UserData{a: "hello"}
// Mashelling struct to jsonByte object to put it into the ledger
userDataJSONBytes, err := json.Marshal(&userdata)
if err != nil {
return shim.Error(err.Error())
}
var err = stub.PutState(params[1],userDataJSONBytes)

Parsing JSON using struct

I'm trying to parse JSON using Go. Can anyone tell me why my code is not working as expected?
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
}
type Person struct {
M Message
}
func get_content() {
body := []byte(`{"person":{"Name":"Alice","Body":"Hello","Time":1294706395881547000}}`)
var data Person
err := json.Unmarshal(body, &data)
if err != nil {
panic(err.Error())
}
fmt.Printf("%v",data.M.Name)
}
func main() {
get_content()
}
I'm expecting it to print the Name.
Go playground Code
There are two problems in the code.
The first one is what #umar-hayat mentioned above -> you are unmarshalling into the data object and you should be aiming at the data.M field.
The second problem is that your JSON's structure doesn't match your struct's structure. Your Person has a single field called M. If we want to represent this as JSON it would look like this:
{
"M": {
"Name": "Joe",
"Body": "Hi",
"time": 2600
}
}
Instead, you have a field called person in your JSON which cannot be matched to any field in your struct. The fact that it's similar to the name of the struct's type doesn't help in any way, I'm afraid.
So, you can either change your JSON and your target:
body := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
var data Person
err := json.Unmarshal(body, &data.M)
Or just your JSON:
body := []byte(`{"M":{"Name":"Alice","Body":"Hello","Time":1294706395881547000}}`)
var data Person
err := json.Unmarshal(body, &data)
But it's essential that the names of the fields in your JSON match the names of the fields in your struct. Or, as mentioned by Konstantinos, you can use tags in order to specify particular names with which your struct's fields will be represented in the JSON.
You might find this helpful: https://gobyexample.com/json
Here is how to Unmarshel JSON to the struct. you can check it on Go Playground here:
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
}
type Person struct {
M Message
}
func get_content() {
body := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
var data Person
err := json.Unmarshal(body, &data.M)
if err != nil {
panic(err.Error())
}
fmt.Printf(data.M.Name)
}
func main() {
get_content()
}
Replace data with data.M in below line.
err := json.Unmarshal(body, &data)
As long as you intent to map Json keys on structs whose fields have different names you should add tags:
type Message struct {
Name string `json:"Name"`
Body string `json:"Body"`
Time int64 `json:"Time"`
}
type Person struct {
M Message `json:"person"`
}
You can find more information here
In addition this answer explains in an nutshell the purpose of tags in go.

create array of struct from by decoding a JSON file in go

All I looking to do is to create an array of struct Response from a json encoded file.
the file that contains json data looks like this.
cat init.txt
{"events": [{"action":"cpr","id":69,"sha1":"abc","cpr":"cpr_data0"},{"action":"cpr","id":85,"sha1":"def","cpr":"cpr_data1"}]}
The way I have gone about approaching this is
I created a response of type map[string][]Response
.. decoded the JSON from the file
.. created a responseStruct of type []Response
But somehow when I inspect the Value they all look 0 or empty
map[events:[{ 0 } { 0 }]
What is wrong with the approach mentioned above.
type Response struct {
action string `json:"action"`
id int64 `json:"id"`
sha1 string `json:"sha1"`
cpr string `json:"cpr"`
}
func main() {
file, err := os.Open("init.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
var response map[string][]Response
err = json.NewDecoder(file).Decode(&response)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
var responseArray []Response
responseArray = response["events"]
for _, responseStruct := range responseArray {
fmt.Println("id =", responseStruct.id)
fmt.Println("action =", responseStruct.action)
fmt.Println("sha1 = ", responseStruct.sha1)
fmt.Println("cpr =", responseStruct.cpr)
fmt.Println("==========")
}
fmt.Println(response)
}
Well If I modify the struct to look like this it works
type Response struct {
Action string `json:"action"`
ID int64 `json:"id"`
Sha1 string `json:"sha1"`
Cpr string `json:"cpr"`
}
So my question is this how the stuff would work, can't I get the above code to work in the way it is?
Go has the notion of public and private fields in objects, and the only distinction is whether they start with an initial capital letter or not. This applies to not just code modules but also the reflect package and things that use it. So in your initial version
type Response struct {
action string `json:"action"`
...
}
nothing outside your source package, not even the "encoding/json" module, can see the private fields, so it can't fill them in. Changing these to public fields by capitalizing Action and the other field names makes them visible to the JSON decoder.
Lowercase struct elements in golang are private, Hence json decoder (which is an external package) can't access them.
Its able to create the struct object but not able to set values. They appear zero because they 0 is default value.

aws-sdk-go dynamodbattribute unmarshal list of maps not unmarshalling

I have a Dynamodb Table that has the following struct:
type StatusItem struct {
requestStatus string
timestamp string
RequestId string
}
I have the code as such:
items := []StatusItem{}
err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &items)
if err != nil {
exitWithError(fmt.Errorf("failed to unmarshal Query result items, %v", err))
}
// Print out the items returned
for i, item := range items {
fmt.Println(i)
fmt.Printf(item.RequestId, item.requestStatus)
}
However, items is empty.
when i put a watcher in items, i can see the it unmarshals to the struct, but all the values are empty.
if i do:
log.println(result.items) i can see the values there (though a bit ugly)
%!(EXTRA string=)[map[timestamp:{
S: "2018-04-18 12:04:43.761"
} RequestId:{
S: "9"
} requestStatus:{
S: "REQUEST_RECEIVED"
}]
what am i doing wrong?
Thanks to #Peter for pointing me in the right direction.
The solution is simple (though rather dumb that that was the case).
as noted in : https://til.hashrocket.com/posts/3512417fb0-private-vs-public-struct-members
lowercase starting members are private, and Capital was public.
in order to get it working correctly, i needed to update my struct from:
type StatusItem struct {
requestStatus string
timestamp string
RequestId string
}
to:
type StatusItem struct {
RequestStatus string
Timestamp string
RequestId string
}

Resources