Using function instead of method in template - go

I use the following code
var data struct {
File FZR
API API
}
const tmpl = `
{{- range .File.Modules}}
# in context of {{.Name}}
echo {{.EchoText}}
{{end}}`
func EchoText(m mts) string {
return m.Type
}
and like this its not working, now I change it to
func (m mts) EchoText() string {
return m.Type
}
it will work but I want to make it work with the first option, how can I do it ?
I mean update the template ...
update: as approva answer https://golang.org/pkg/text/template/#example_Template_func
but there is only string, how can I register the EchoText

As #mkopriva suggested in his first comment you just have to reference all your functions into a template.FuncMap where you establish the mapping from names to functions
Then into the template you just need to call them passing them the mts as parameter.
package main
import (
"log"
"os"
"text/template"
)
type mts struct {
Type1 string
Type2 string
}
func main() {
funcMap := template.FuncMap{
"myFunc1": EchoType1,
"myFunc2": EchoType2,
}
const templateText = `
Input: {{printf "%q" .}}
Output1:{{myFunc1 .}}
Output2:{{myFunc2 .}}
`
tmpl, err := template.New("myFuncTest").Funcs(funcMap).Parse(templateText)
if err != nil {
log.Fatalf("parsing: %s", err)
}
myMts := mts{Type1: "myType1", Type2: "myType2"}
err = tmpl.Execute(os.Stdout, myMts)
if err != nil {
log.Fatalf("execution: %s", err)
}
}
func EchoType1(m mts) string {
return m.Type1
}
func EchoType2(m mts) string {
return m.Type2
}
This will produce the following output :
Input: {"myType1" "myType2"}
Output1:myType1
Output2:myType2

Related

How does Golang accept uncertain values

Back-end return values are not fixed, sometimes:
{"application": {"instance": [{"instanceId": "v1"}, {"instanceId": "v2"}]}}
or sometimes:
{"application": {"instance": {"instanceId": "v"}}}
how should I take out the corresponding instanceId value?
package main
import (
"encoding/json"
"fmt"
)
type Application struct {
Application struct {
Instance json.RawMessage `json:"instance"`
} `json:"application"`
}
func main() {
a := `{"application": {"instance": {"instanceId": "v"}}}`
//a := `{"application": {"instance": [{"instanceId": "v1"}, {"instanceId": "v2"}]}} `
var p Application
errJson := json.Unmarshal([]byte(a), &p)
if errJson != nil {
fmt.Printf("errJson")
}
fmt.Printf("type:%T", p.Application.Instance)
}
Since the 2 value types clash (one a struct another a slice of structs) it gets messy to encapsulate this into a single type even using catch-all solutions like interface{}.
The simplest solution is present two distinct types and marshal into either to see which "works":
func unmarsh(body []byte) (*type1, *type2, error) {
var (
t1 type1
t2 type2
)
err := json.Unmarshal(body, &t1)
if err == nil {
return &t1, nil, nil
}
err = json.Unmarshal(body, &t2)
if err == nil {
return nil, &t2, nil
}
return nil, nil, err
}
and in your example the two types would be:
type type1 struct {
Application struct {
Instance []struct {
InstanceID string `json:"instanceId"`
} `json:"instance"`
} `json:"application"`
}
type type2 struct {
Application struct {
Instance struct {
InstanceID string `json:"instanceId"`
} `json:"instance"`
} `json:"application"`
}
Working example:
https://play.golang.org/p/Kma32gWfghb
A cleaner solution would be a custom unmarshaler:
type Instances []Instance
func (i *Instances) UnmarshalJSON(in []byte) error {
if len(in)>0 && in[0]=='[' {
var a []Instance
if err:=json.Unmarshal(in,&a); err!=nil {
return err
}
*i=a
return nil
}
var s Instance
if err:=json.Unmarshal(in,&s) ; err!=nil {
return err
}
*i=[]Instance{s}
return nil
}
This would unmarshal an object into a slice of 1.
A more compact solution is provided by #mkopriva:
func (i *Instances) UnmarshalJSON(in []byte) error {
if len(in) > 0 && in[0] == '[' {
return json.Unmarshal(in, (*[]Instance)(i))
}
*i = Instances{{}}
return json.Unmarshal(in, &(*i)[0])
}

MongoDB distinct query and $in with Go

I'm having trouble with the distinct query in MongoDB.
I can write it in Mongo shell, it works but I don't know how to implement it in Go code.
Here is my Mongo shell code
db.getCollection('company_role_function').distinct("rolecode", {rolecode : {
$in: ['DHBK_ROLE_01','DHBK_ROLE_03' ] },productid:'IOT_Platform'
})
And here is my Go code
1.profile.go
type CompanyRoleFunction struct {
Rolecode string `json:"rolecode"`
Productid string `json:"productid"`
Functioncode string `json:"functioncode"`
Comid string `json:"comid"`
}
repository.go
package repository
import "bitbucket.org/cloud-platform/vnpt-sso-usermgnt/model"
type IProfileRepository interface {
FindRoleByUserProduct(string) (*model.CompanyRoleFunction, error)
}
mongo_driver.go
package repository
import (
"bitbucket.org/cloud-platform/vnpt-sso-usermgnt/model"
"go.mongodb.org/mongo-driver/bson"
"gopkg.in/mgo.v2"
)
type ProfileRepositoryMongo struct {
db *mgo.Database
collection string
}
func NewProfileRepositoryMongo(db *mgo.Database, collection string) *ProfileRepositoryMongo {
return &ProfileRepositoryMongo{
db: db,
collection: collection,
}
}
//I HAVE TROUBLE HERE
func (r *ProfileRepositoryMongo) FindRoleByUserProduct(rolecode arr[]string) (*model.CompanyRoleFunction, error) {
var companyRoleFunction model.CompanyRoleFunction
//I HAVE TROUBLE HERE
err := r.db.C(r.collection).Find(bson.M{"username": username}).One(&companyRoleFunction)
//I HAVE TROUBLE HERE
if err != nil {
return nil, err
}
return &companyRoleFunction, nil
}
Try the below code for distinct in mgo
package main
import (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"gopkg.in/mgo.v2"
)
type Result struct {
Rolecode string `json:"rolecode"`
Productid string `json:"productid"`
Functioncode string `json:"functioncode"`
Comid string `json:"comid"`
}
type Results []Result
func main() {
//delete1("GV_BMVT")
//update("GV_BMVT")
check()
}
func check() {
session, err := mgo.Dial("mongodb://casuser:Mellon#222.255.102.145:27017/users")
if err != nil {
panic(err)
}
c := session.DB("users").C("company_role_function")
results := []string{}
roleArray := []string{"DHBK_ROLE_01,", "DHBK_ROLE_03"}
err = c.Find(bson.M{"rolecode": bson.M{"$in": roleArray}, "productid": "IOT_Platform"}).Distinct("rolecode", &results)
if err != nil {
panic(err)
}
fmt.Println(results)
}

How to omit empty json fields using json.decoder

I try to understand why both functions return the same output.
As far as I understood, the point of omit empty is to not add that key to the result struct.
I wrote this example, I was expecting the first output not to have the "Empty" key, but for some reason its value still shows as 0.
package main
import (
"encoding/json"
"fmt"
"strings"
)
type agentOmitEmpty struct {
Alias string `json:"Alias,omitempty"`
Skilled bool `json:"Skilled,omitempty"`
FinID int32 `json:"FinId,omitempty"`
Empty int `json:"Empty,omitempty"`
}
type agent struct {
Alias string `json:"Alias"`
Skilled bool `json:"Skilled"`
FinID int32 `json:"FinId"`
Empty int `json:"Empty"`
}
func main() {
jsonString := `{
"Alias":"Robert",
"Skilled":true,
"FinId":12345
}`
fmt.Printf("output with omit emtpy: %v\n", withEmpty(strings.NewReader(jsonString)))
// output with omit emtpy: {Robert true 12345 0}
fmt.Printf("output regular: %v\n", withoutEmpty(strings.NewReader(jsonString)))
// output without omit: {Robert true 12345 0}
}
func withEmpty(r *strings.Reader) agentOmitEmpty {
dec := json.NewDecoder(r)
body := agentOmitEmpty{}
err := dec.Decode(&body)
if err != nil {
panic(err)
}
return body
}
func withoutEmpty(r *strings.Reader) agent {
dec := json.NewDecoder(r)
body := agent{}
err := dec.Decode(&body)
if err != nil {
panic(err)
}
return body
}
You need to define Empty as *int so it will be replaced with nil when there is no value. Then it will not be saved in the database.

How to handle Response JSON have custom field with out key?

Query Api and response a custom JSON, how to Unmarshal it. the sample JSON:
{"14AcKEr19gHJvgwQhK7sfFm6YJGmoZZoqu": {
"final_balance": 61914248289,
"n_tx": 3472,
"total_received": 3479994002972
}}
The key is a hex string. So how to handle it with golang convention, anyone can help me?
Below is my try test code:
c.OnResponse(func(r *colly.Response) {
jsonData := r.Body
fmt.Println(string(jsonData))
fmt.Println("==================")
//parse bitcoin json
jsonMap := make(map[string]interface{})
err := json.Unmarshal([]byte(jsonData), &jsonMap)
if err != nil {
panic(err)
}
fmt.Println(jsonMap)
dumpMap("", jsonMap)
})
func dumpMap(space string, m map[string]interface{}) {
for k, v := range m {
if mv, ok := v.(map[string]interface{}); ok {
fmt.Printf("{ \"%v\": \n", k)
dumpMap(space+"\t", mv)
fmt.Printf("}\n")
} else {
fmt.Printf("%v %v : %v\n", space, k, v)
}
}
}
and go run cmd/main.go, the console is print here:
{"14AcKEr19gHJvgwQhK7sfFm6YJGmoZZoqu": {
"final_balance": 75494521080,
"n_tx": 3493,
"total_received": 3493574275763
}}
==================
map[14AcKEr19gHJvgwQhK7sfFm6YJGmoZZoqu:map[n_tx:3493 total_received:3.493574275763e+12 final_balance:7.549452108e+10]]
{ "14AcKEr19gHJvgwQhK7sfFm6YJGmoZZoqu":
final_balance : 7.549452108e+10
n_tx : 3493
total_received : 3.493574275763e+12
}
Do I need customised unmarshal func to get string key? If I use 14AcKEr19gHJvgwQhK7sfFm6YJGmoZZoqu as key I can't easily to access. I just want to know how handle it.
you can unmarshal it into map, so you can get generated key as a key of map
https://play.golang.org/p/IfEjjvKakpu
package main
import (
"encoding/json"
"fmt"
"log"
)
var input = `{"14AcKEr19gHJvgwQhK7sfFm6YJGmoZZoqu": {
"final_balance": 61914248289,
"n_tx": 3472,
"total_received": 3479994002972
}}`
type object struct {
FinalBalance uint64 `json:"final_balance"`
NTX uint64 `json:"n_tx"`
TotalReceived uint64 `json:"total_received"`
}
func main() {
var result map[string]object;
err := json.Unmarshal([]byte(input), &result);
if err != nil {
log.Fatal(err)
}
fmt.Printf("result: %+v", result)
// result: map[14AcKEr19gHJvgwQhK7sfFm6YJGmoZZoqu:{FinalBalance:61914248289 NTX:3472 TotalReceived:3479994002972}]
}

How to compare Map for common keys and print the output?

I have the following code which generates the following output
code:
package main
import (
"html/template"
"os"
)
type EntetiesClass struct {
Name string
Value int32
}
// In the template, we use rangeStruct to turn our struct values
// into a slice we can iterate over
var htmlTemplate = `{{range $index, $element := .}}
{{range $element}}{{.Name}}={{.Value}}
{{- end}}
{{- end}}`
func main() {
data := map[string][]EntetiesClass{
"Container1": {{"Test", 15}},
"Container2": {{"Test", 15}},
}
t := template.New("t")
t, err := t.Parse(htmlTemplate)
if err != nil {
panic(err)
}
err = t.Execute(os.Stdout, data)
if err != nil {
panic(err)
}
}
link: https://play.golang.org/p/yM9_wWmyLY
Output:
Test=15
Test=15
I want to compare the Container1 with Container2 and if they have common key, i just want to print output only once.
Output:
Test=15
How can i achieve this? Any help is appreciated?
I can think of two ways to do this:
Dedup your data before passing to the template execution
This means you can pre-process the data before passing to t.Execute to eliminate duplicates. You can do this using something like:
m := map[EntitiesClass]bool{}
for _, ... {
m[ec] = true
// Or maybe you want to aggregate "Container 1"
// and "Container 2" in some way
}
Then you can just pass the processed data and the template itself remains virtually unchanged
Add a custom function for your template
This means you can add a normal go function that receives as many EntitiesClass as you like and returns them deduplicated (perhaps with the mechanism from option 1).
You can even do something like:
{{if not customHaveSeenThisValueBefore }}
...
{{ endif }}
For your simple example I would choose option 1, it seems easiest to leave templates very simple.
This applies some filtering logic. I'm using text/template because this code isn't using a server. It will work the same with html/template. When appending to slices of structs pointers have to be used. If you don't know why see here. func isInSlice() bool needs to be modded to your needs. This serves as an example.
package main
import (
"os"
"text/template"
)
type Entitie struct {
Results []*Data
}
type Data struct {
Name string
Value int32
}
// Change this function to mod how to filter
func (e *Entitie) isInSlice(name string) bool {
for _, item := range e.Results {
if item.Name == name {
return true
}
}
return false
}
func (e *Entitie) AddData(name string, value int32) {
if !e.isInSlice(name) {
e.Results = append(e.Results, &Data{Name: name, Value: value})
}
}
// In the template, we use rangeStruct to turn our struct values
// into a slice we can iterate over
var template = `
{{range $i, $e := .Data.Results}}
{{$e.Name}} = {{$e.Value}}
{{end}}
`
func main() {
data := make(map[string]Entitie)
var entities Entitie
entities.AddData("test", 15)
entities.AddData("test", 15)
entities.AddData("test2", 15)
t := template.New("t")
t, err := t.Parse(template)
if err != nil {
panic(err)
}
data["Data"] = entities
err = t.Execute(os.Stdout, data)
if err != nil {
panic(err)
}
}

Resources