How to parse strings of json object seperated by comma but not enclosed by array - go

In the below sample elasticsearch doument created by the alerting rule, contains 3 comma seperated string of json objects under hits but they are NOT contained in an array [], so in Go unable to parse them.
Can someone help me parse the hits documents
[map[_id:2s3kfXoB2vuM1J-EwpE7 _index:alert-X _score:%!s(float64=1)
_source:
map[#timestamp:2021-07-06T22:16:21.818Z
alert_name:alert events login
hits:
{"_index":".ds-logs-events-2021.06.30-000005","_type":"_doc","_id":"S83kfXoB2vuM1J-Eo4_v", ...
{"_index":".ds-logs-events-2021.06.30-000005","_type":"_doc","_id":"Ss3kfXoB2vuM1J-Eo4_v",...
{"_index":".ds-logs-events-2021.06.30-000005","_type":"_doc","_id":"N83kfXoB2vuM1J-EiI2l",...
rule_id:cfb85000-db0e-11eb-83e0-bb11d01642c7
]
Model
type Alert struct {
Alert string `json:"alert_name"`
Hits []*Event `json:"hits"`
}
type Event struct {
Model string
Action string
}
Following offical example
Using official go-elasticsearch and easyjson

Concatenated the json string with the array block and was able to Unmarshal it
hitsArray := "[" + alert.Source.Hits + "]"
var hits []model.AlertHits
json.Unmarshal([]byte(hitsArray), &hits)
for _, hit := range hits {
log.Printf("hit %s action %s", hit.ID, hit.Source.Message.Action)
}
model.go
type AlertHits struct {
ID string `json:"_id"`
Source Event `json:"_source"`
}
type Event struct {
Message Message `json:"message"`
}
type Message struct {
Action string `json:"action"`
Model string `json:"model"`
}

Related

Go return struct as JSON in HTTP request

I've defined the following struct in Go:
type repoStars struct {
name string
owner string
stars int
}
And I've created an array repoItems := []repoStars{} which has multiple items of the struct above.
This is how repoItems looks like:
I'm trying to return those items as a JSON response:
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(repoItems)
And it seems empty
What am I doing wrong here?
If the struct fields start with a lower case letter it means unexported. All unexported fields won't be serialised by the encoder.
Change it to capital first letter.
type repoStars struct {
Name string
Owner string
Stars int
}

How can I get this type of data in Go lang?

This is API response data, looks like this.
{
"result":1,
"message":"",
"pds": [
{
"state":"Y",
"code":13,
"name":"AAA",
"price":39900,
},
{
"state":"Y",
"code":12,
"name":"BBB",
"price":38000,
}
],
"request":
{
"op":"new",
}
}
How can I get this data in Go lang?
I tried json.Unmarshall and get with map[string]interface{} but it looks like I used the wrong type to get the data.
Should I use structure??
You need to create a struct to handle this properly if you don't want the json.Unmarshall output to be a map[string]interface{}.
If you map this JSON object to a Go struct you find the following structure:
type APIResponse struct {
Result int `json:"result"`
Message string `json:"message"`
Pds []struct {
State string `json:"state"`
Code int `json:"code"`
Name string `json:"name"`
Price float64 `json:"price"`
} `json:"pds"`
Request struct {
Op string `json:"op"`
} `json:"request"`
}
You also can find a great tool to convert JSON objects to Go structs here
If you don't want to use the native json.Unmarshall, here's a good option to use the package go-simplejson.
Documentation

Dynamically Create Structs in Golang

So I am working with an external API, whose responses I wanted to parse. The incoming responses are of a fixed format i.e.
type APIResponse struct {
Items []interface{} `json:"items"`
QuotaMax int `json:"quota_max"`
QuotaRemaining int `json:"quota_remaining"`
}
So for each response I am parsing the items. Now the items can be of diff types as per the request. It can be a slice of sites, articles, etc. Which have their individual models. like:
type ArticleInfo struct {
ArticleId uint64 `json:"article_id"`
ArticleType string `json:"article_type"`
Link string `json:"link"`
Title string `json:"title"`
}
type SiteInfo struct {
Name string `json:"name"`
Slug string `json:"slug"`
SiteURL string `json:"site_url"`
}
Is there any way, when parsing the input define the type of Items in APIResponse. I don't want to create separate types for individual responses.
Basically want to Unmarshall any incoming response into the APIResponse struct.
Change type of the Items field to interface{}:
type APIResponse struct {
Items interface{} `json:"items"`
...
}
Set the response Items field to pointer of the desired type. Unmarshal to the response:
var articles []ArticleInfo
response := APIResponse{Items: &articles}
err := json.Unmarshal(data, &response)
Access the articles using variable articles.
Run an example on the playground.

Better way of decoding json values

Assume a JSON object with the general format
"accounts": [
{
"id": "<ACCOUNT>",
"tags": []
}
]
}
I can create a struct with corresponding json tags to decode it like so
type AccountProperties struct {
ID AccountID `json:"id"`
MT4AccountID int `json:"mt4AccountID,omitempty"`
Tags []string `json:"tags"`
}
type Accounts struct {
Accounts []AccountProperties `json:"accounts"`
}
But the last struct with just one element seems incorrect to me. Is there a way I could simply say type Accounts []AccountProperties `json:"accounts"` instead of creating an entire new struct just to decode this object?
You need somewhere to store the json string accounts. Using a:
var m map[string][]AccountProperties
suffices, though of course you then need to know to use the string literal accounts to access the (single) map entry thus created:
type AccountProperties struct {
ID string `json:"id"`
MT4AccountID int `json:"mt4AccountID,omitempty"`
Tags []string `json:"tags"`
}
func main() {
var m map[string][]AccountProperties
err := json.Unmarshal([]byte(data), &m)
fmt.Println(err, m["accounts"])
}
See complete Go Playground example (I had to change the type of ID to string and fix the missing { in the json).
As Dave C points out in comments, this is no shorter than just using an anonymous struct type:
var a struct{ Accounts []AccountProperties }
in terms of the Unmarshall call (and when done this way it's more convenient to use). Should you want to use an anonymous struct like this in a json.Marshall call, you'll need to tag its single element to get a lowercase encoding: without a tag it will be called "Accounts" rather than "accounts".
(I don't claim the map method to be better, just an alternative.)

Golang Decode a BSON with special character Keys to a struct

I have a Golang struct called Person where all the properties have to be exported:
type Person struct {
Id string
Name string
}
Now I need to encode my MongoDB BSON response to this Person struct. The BSON looks like:
{
"_id": "ajshJSH78N",
"Name": "Athavan Kanapuli"
}
The Golang code to encode the BSON is:
mongoRecord := Person{}
c := response.session.DB("mydb").C("users")
err := c.Find(bson.M{"username": Credentials.Username, "password": Credentials.Password}).One(&mongoRecord)
The Problem:
_id is not getting encoded into Id
If I change the Person property into _Id, then it won't be exported.
How can I solve this problem?
Define your struct with json tag-
type Person struct {
Id string `json:"_id"`
Name string // this field match with json, so mapping not need
}
I tried to put a json tag like ,
type Person struct {
Id string `json:"_id"`
Name string // this field match with json, so mapping not need
}
But still it didn't work. Because the Mongodb returns '_id' which is of type bson.ObjectId . Hence changing the Struct tag to bson:"_id" and the type of the Person struct has been changed from string to bson.ObjectId. The changes done are as follows ,
type Person struct {
Id bson.ObjectId `bson:"_id"`
Name string
UserName string
IsAdmin bool
IsApprover bool
}
And It works!

Resources