I want to validate my input map of array with govalidator.ValidateMap.
Please can someone suggest for Sample mapTemplate for map of array.
Please find below the code snippet.
Thanks in Advance
package main
import (
"fmt"
"github.com/asaskevich/govalidator"
)
func main() {
var mapTemplate = map[string]interface{}{
"name": "required,alpha",
"categories": []interface{}{",alpha"}, //error: map validator has to be either map[string]interface{} or string; got []interface {}
}
var inputMap = map[string]interface{}{
"name": "Prabhu",
"categories": []interface{}{"category1", "category2"},
}
result, err := govalidator.ValidateMap(inputMap, mapTemplate)
if err != nil {
fmt.Println("error: " + err.Error())
}
fmt.Printf("result : %v\n", result)
for _, v := range inputMap["categories"].([]interface{}) {
fmt.Printf("category : %v\n", v)
}
}
It seems validation of slices has not yet been implemented. There is no check in the What to contribute list for slices/arrays.
You can however use the function ValidateArray to iterate over a slice and validate its members.
govalidator.ValidateArray(inputMap["categories"], func(val interface{}, i int) bool {
valStr, ok := val.(string)
if !ok {
return false
}
return govalidator.IsAlpha(valStr)
})
Related
I'm trying to seed my Postgres database as functionally. In my case, SeedSchema() function can take any type struct. So I define a interface and create functions to my structs which will seed. I tried with generics and without.
When I unmarshall any json array from file as byte array, json.Unmarshall method change my tempMember member of struct. Exp, models.Term to map[string]interface{}. I've used unmarshall before this function and I've not seen like this situation.
Here is my SeedSchema() function:
func (db *Database) SeedSchema(models ...globals.Seeder[any]) error {
var (
subjects []globals.Seeder[any]
fileByte []byte
err error
// tempMember any
)
if len(models) == 0 {
subjects = seederModelList
} else {
subjects = models
}
for _, model := range subjects {
fileName, tempMember := model.Seed()
fmt.Printf("%+v\n", reflect.TypeOf(tempMember)) //1
if fileByte, err = os.ReadFile("db/seeds/" + fileName); err != nil {
fmt.Println(err)
return err
}
if err = json.Unmarshal(fileByte, &tempMember); err != nil {
fmt.Println(err)
return err
}
fmt.Printf("%+v\n", reflect.TypeOf(tempMember)) //2
}
return nil
}
First print returns []models.AirportCodes and the second []interface {}.
Here is my interface and model:
func (AirportCodes) Seed() (string, any) {
return "airport_codes.json", []AirportCodes{}
}
type Seeder[T any] interface {
Seed() (string, T)
// Seed(*gorm.DB) error
TableName() string
}
seederModelList = []globals.Seeder[any]{
m.AirportCodes{},
m.Term{},
}
After a few weeks, I have looking for solve this problem and look unmarshaler interfaces and examples. Then Like what icza said, I started to look over the my code that convention between types and I solved like this. If you guys have better answer than mine, please add answer.
Data:
[
{
"id":1,
"name":"Term 1",
"content": [
"a1",
"a2",
"a3"
]
}
]
Result:
[{ID:1 Name:Term 1 Content:[a1 a2 a3]}]
UnmarshalJSON Function:
func (term *Term) UnmarshalJSON(data []byte) error {
tempMap := map[string]interface{}{}
if err := json.Unmarshal(data, &tempMap); err != nil {
return err
}
*term = Term{
Name: tempMap["name"].(string),
}
if tempMap["content"] != nil {
for _, v := range tempMap["content"].([]interface{}) {
(*term).Content = append((term).Content, v.(string))
}
}
return nil
}
Thank you for comments.
I'm trying to sort the result in golang chaincode, but the result is random, below is my chaincode sample:
package main
import (
"bytes"
"encoding/json"
"fmt"
"time"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
type itemStruct struct {
ID string `json:"id"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
func createItem(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 3 {
return shim.Error(fmt.Sprintf("Expecting %v arguments {id, status, created_at}, but got %v", 3, len(args)))
}
itemID := args[0]
if len(itemID) == 0 {
return shim.Error("id field is required")
}
status := args[1]
if len(status) == 0 {
return shim.Error("status field is required")
}
createdAt, err := time.Parse(time.RFC3339, args[2])
if err != nil {
return shim.Error("created_at is not a valid datetime string")
}
item := itemStruct{
ID: itemID,
CreatedAt: createdAt,
}
itemAsJSONBytes, err := json.Marshal(item)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(itemAsJSONBytes)
}
func getPendingItems(stub shim.ChaincodeStubInterface, args []string) peer.Response {
var bookmark string
if len(args) > 0 && len(args[0]) > 0 {
bookmark = args[0]
}
queryString := `{
"selector": {
"status": "pending"
},
"sort": [
{"created_at": "desc"}
]
}`
result, pagination, err := queryWithPagination(stub, queryString, 20, bookmark)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(constructResponse(result, pagination).Bytes())
}
func queryWithPagination(stub shim.ChaincodeStubInterface, queryString string, pageSize int32, bookmark string) (map[string]string, string, error) {
var pagination string
iterator, meta, err := stub.GetQueryResultWithPagination(queryString, pageSize, bookmark)
if err != nil {
return nil, pagination, err
}
defer iterator.Close()
result, err := iterateResult(iterator)
if err != nil {
return nil, pagination, err
}
pagination = fmt.Sprintf(`{"count": %v, "next_page_token": "%v"}`, meta.FetchedRecordsCount, meta.Bookmark)
return result, pagination, nil
}
func constructResponse(items map[string]string, pagination string) *bytes.Buffer {
// buffer is a JSON array containing QueryResults
var buffer bytes.Buffer
if len(pagination) > 0 {
buffer.WriteString(`{"data":`)
}
buffer.WriteString(`[`)
bArrayMemberAlreadyWritten := false
for _, val := range items {
// Add a comma before array members, suppress it for the first array member
if bArrayMemberAlreadyWritten == true {
buffer.WriteString(",")
}
buffer.WriteString(val)
bArrayMemberAlreadyWritten = true
}
buffer.WriteString("]")
if len(pagination) > 0 {
buffer.WriteString(`,"pagination":`)
buffer.WriteString(pagination)
buffer.WriteString("}")
}
return &buffer
}
func iterateResult(iterator shim.StateQueryIteratorInterface) (map[string]string, error) {
result := map[string]string{}
for iterator.HasNext() {
queryResponse, err := iterator.Next()
if err != nil {
return nil, err
}
result[queryResponse.Key] = string(queryResponse.Value)
}
return result, nil
}
// SmartContract : Smart contract struct
type SmartContract struct {
}
// Init : This method is called when chaincode is initialized or updated.
func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) peer.Response {
return shim.Success(nil)
}
// Invoke : This method is called when any transaction or query fired
func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Retrieve the requested Smart Contract function and arguments
function, args := stub.GetFunctionAndParameters()
// Route to the appropriate handler function to interact with the ledger appropriately
// Transactions
if function == "createItem" {
return createItem(stub, args)
}
// Queries
if function == "getItems" {
return getItems(stub, args)
}
return shim.Error("Invalid function")
}
func main() {
// Create a new Smart Contract
err := shim.Start(new(SmartContract))
if err != nil {
fmt.Printf("Error creating new Smart Contract: %s", err)
}
}
It creates and asset which can have different status based on what's passed, and I have defined one query function which fetches only pending items.
I have applied sort, but the result is still random, can anyone help me here and guide me where I'm going wrong in this?
the sort field has to be present in the selector !
something like:
queryString := `{
"selector": {
"created_at": "$gt": null
"status": "pending"
},
"sort": [
{"created_at": "desc"}
]
}`
or (range)
queryString := `{
"selector": {
"created_at": {
"$gt": "2015-01-01T00:00:00Z",
"$lt": "2019-01-01T00:00:00Z"
},
"status": "pending"
},
"sort": [
{"created_at": "desc"}
]
}`
Per the docs - to use sorting, ensure that:
At least one of the sort fields is included in the selector.
There is an index already defined, with all the sort fields in the same
order.
Each object in the sort array has a single key.
If an object in the sort array does not have a single key, the resulting sort order is implementation specific and might change.
Also, as an fyi Find does not support multiple fields with different sort orders, so the directions must be either all ascending or all descending.
See also CouchDB sort doesn't work
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}]
}
I have a project function which returns a slice containing the field values by name of each struct or map in an input slice. I am having trouble with case where the input slice contains pointers to structs. I have setup a recursive function to operate on the value, but need to know how to convert from kind reflect.Ptr to the underlying reflect.Struct. How is this done? Any other design recommendations are appreciated. I am still a bit new to Go.
Here is the code:
func project(in []interface{}, property string) []interface{} {
var result []interface{}
var appendValue func(list []interface{}, el interface{})
appendValue = func(list []interface{}, el interface{}) {
v := reflect.ValueOf(el)
kind := v.Kind()
if kind == reflect.Ptr {
// How do I get the struct behind this ptr?
// appendValue(list, el)
} else if kind == reflect.Struct {
result = append(result, v.FieldByName(property).Interface())
} else if kind == reflect.Map {
result = append(result, el.(map[string]interface{})[property])
} else {
panic("Value must be a struct or map")
}
}
for _, el := range in {
appendValue(result, el)
}
return result
}
... and the test cases:
func Test_project(t *testing.T) {
cases := map[string]struct {
input []interface{}
property string
expected []interface{}
}{
"simple-map": {
[]interface{}{
map[string]interface{}{
"a": "a1",
},
},
"a",
[]interface{}{"a1"},
},
"simple-struct": {
[]interface{}{
simpleStruct{
A: "a1",
},
},
"A",
[]interface{}{"a1"},
},
// THIS ONE FAILS
"simple-struct-ptr": {
[]interface{}{
&simpleStruct{
A: "a1",
},
},
"A",
[]interface{}{"a1"},
},
}
for k, v := range cases {
t.Run(k, func(t *testing.T) {
got := project(v.input, v.property)
if !reflect.DeepEqual(got, v.expected) {
t.Fatalf("Expected %+v, got %+v", v.expected, got)
}
})
}
}
Use Elem() to go from a reflect.Ptr to the value it points to.
I want to scan AWS DynamoDB table and then pull only a certain value. Here is my code:
package main
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
func main() {
svc := dynamodb.New(session.New(), &aws.Config{Region: aws.String("us-west-2")})
params := &dynamodb.ScanInput{
TableName: aws.String("my_Dynamo_table_name"),
Limit: aws.Int64(2),
}
resp, err := svc.Scan(params)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(resp)
}
and The output is:
{
Count: 2,
Items: [{
update_time: {
N: "1466495096"
},
create_time: {
N: "1465655549"
}
},{
update_time: {
N: "1466503947"
},
create_time: {
N: "1466503947"
}
}],
LastEvaluatedKey: {
Prim_key: {
S: "1234567890"
}
},
ScannedCount: 2
}
Now, I want to retrieve the update_time value for all elements in above output. Here are my attempts:
for _, value := range resp.Items {
fmt.Println(value["create_time"]["N"])
}
and
for _, value := range resp.Items {
fmt.Println(value.create_time.N)
}
and
for _, value := range resp.Items {
fmt.Println(*value.create_time.N)
}
All above attempts error out with /var/tmp/dynamo.go:37: invalid operation: error.
I am from perl/python background and recently started learning golang.
How to retrieve nested map/array values in this case. Also, any reading references would be of great help. My google search did not reveal anything relevant.
The value of resp above is of the type *ScanOutput, which has Items type as []map[string]*AttributeValue.
To access update_time, you can try:
updateTimes := make([]string, 0)
// Items is a slice of map of type map[string]*AttributeValue
for _, m := range resp.Items {
// m is of type map[string]*AttributeValue
timeStrPtr := *m["update_time"].N
updateTimes = append(updateTimes, *timeStrPtr)
}
updateTimes should now contains all the "update_time" values as strings.
More details here.
You should use the dynamodbattribute package. it's cheaper, safer, and more readable.
Following your example:
type Row struct {
CreateTime int `dynamodbav:"create_time"`
UpdateTime int `dynamodbav:"update_time"`
}
// ...
rows := make([]*Row, len(resp.Items))
if err := dynamodbattribute.Unmarshal(resp.Items, &rows); err != nil {
// handle the error
}
// access the data
for _, row := range rows {
fmt.Println(row.CreateTime, row.UpdateTime)
}