Navigation into a map with a string path variable for golang - dictionary

I would like to directly navigate to a value in a map. Lets be more specific with the following go code example which should give me the value of "Walter" directly: (https://play.golang.org/p/tYJsvp39hn)
type Signature struct{
Name string
Signed bool
}
path := "Document.Signatures.1.Name"
map := map[string]interface{}{
"Document": map[string]interface{}{
"Signatures": []interface{}{
Signature{ Name: "Hugo", Signed: false },
Signature{ Name: "Walter", Signed: false },
},
"Otherstuff": "asadwa",
},
"AlsoOtherStuff": "adwaw",
}
// map.giveMe(path)
// even better (if possible:) map.change(path,"ToThisNewValue")
I have searched for solutions, but I can't find any on the internet. Maybe one of you knows how to do this or knows a library to use for me.
Thank you so much in advance!

Quite a lot of reflect calls will be needed if there is no predefined struct.
That being said, you can do it by iterating through the map with type checking on every iteration and handling cases accordingly.
// Splitting the path into keys
keys := strings.Split(path, ".")
var value interface{} = map1
for _, key := range keys {
if value, err = Get(key, value); err != nil {
break
}
}
if err == nil {
fmt.Println("Value:", value)
} else {
fmt.Println("Error:", err)
}
func Get(key string, s interface{}) (v interface{}, err error) {
var (
i int64
ok bool
)
switch s.(type) {
case map[string]interface{}:
if v, ok = s.(map[string]interface{})[key]; !ok {
err = fmt.Errorf("Key not present. [Key:%s]", key)
}
case []interface{}:
if i, err = strconv.ParseInt(key, 10, 64); err == nil {
array := s.([]interface{})
if int(i) < len(array) {
v = array[i]
} else {
err = fmt.Errorf("Index out of bounds. [Index:%d] [Array:%v]", i, array)
}
}
case Signature:
r := reflect.ValueOf(s)
v = reflect.Indirect(r).FieldByName(key)
}
//fmt.Println("Value:", v, " Key:", key, "Error:", err)
return v, err
}
Playground code

Related

cmparing two yaml files and output the difference

I have two yaml files and i want to show the difference between the two. The yaml files are below. I want to display difference between the two yamls.
Like under deletion it should be name:"first" and under updates it should be a full path something like profiles:names:description:"second yaml".
The code below gives me the full object in case of a deeply nested object. How do i get the full path?
YAML1
`
id: 5
name: "first"
repo: "some repo"
profiles:
id: 3
name: default
description: "first yaml"
data:
logs: []
config:
hosts:
url.com: minikube
`
YAML2
`
id: 5
repo: "some repo"
profiles:
id: 3
name: default
description: "second yaml"
data:
logs: []
config:
hosts:
url.com: minikube
`
`
package main
import (
"fmt"
"io/ioutil"
"log"
"reflect"
"gopkg.in/yaml.v3"
)
type Modifications struct {
addition map[string]interface{}
deletion map[string]interface{}
update map[string]interface{}
}
func NewModifications() *Modifications {
var m Modifications
m.addition = make(map[string]interface{})
m.deletion = make(map[string]interface{})
m.update = make(map[string]interface{})
return &m
}
func compareYaml(modifications *Modifications) {
// read file
yfile1, err := ioutil.ReadFile("./old.yaml")
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
yfile2, err := ioutil.ReadFile("./new.yaml")
if err != nil {
log.Fatal(err)
//return
}
// unmarshal ymal
data1 := make(map[string]interface{})
if err := yaml.Unmarshal(yfile1, &data1); err != nil {
fmt.Println(err)
log.Fatal(err)
//return
}
data2 := make(map[string]interface{})
if err := yaml.Unmarshal(yfile2, &data2); err != nil {
fmt.Println(err)
log.Fatal(err)
//return
}
// from this we can iterate the key in data2 then check whether it exists in data1
// if so then we can update the value in data2
// iterate key1 in data2
for key1, value1 := range data1 {
// check whether key1 exists in data2
if _, exists := data2[key1]; exists {
// see if it is an update
if reflect.DeepEqual(data2[key1], value1) {
delete(data2, key1)
} else {
modifications.update[key1] = data2[key1]
delete(data2, key1)
}
} else {
modifications.deletion[key1] = value1
}
}
for k, v := range data2 {
modifications.addition[k] = v
}
}
func main() {
modifications := NewModifications()
compareYaml(modifications)
fmt.Println("************")
fmt.Println("adddition")
fmt.Println(modifications.addition)
fmt.Println("************")
fmt.Println("deletion")
fmt.Println(modifications.deletion)
fmt.Println("************")
fmt.Println("update")
fmt.Println(modifications.update)
fmt.Println("************")
}
`
Playground
You may want to use the compare package:
https://github.com/google/go-cmp
There is a cmp.Diff that allows you to compare two objects and will return the diff in string format.

Using chromedp to render PNG object from HTML string

I am very new to Chromedp. I know how to render image from an url for Chromdp but is there a way to render it from a HTML string itself using Chromedp? I am able to convert an url to image using the following:
func fullScreenshot(urlstr string, quality int64, res *[]byte) chromedp.Tasks {
return chromedp.Tasks{
chromedp.Navigate(urlstr),
chromedp.Sleep(3 * time.Second),
chromedp.ActionFunc(func(ctx context.Context) error {
// get layout metrics
_, _, contentSize, err := page.GetLayoutMetrics().Do(ctx)
if err != nil {
return err
}
width, height := int64(math.Ceil(contentSize.Width)), int64(math.Ceil(contentSize.Height))
// force viewport emulation
err = emulation.SetDeviceMetricsOverride(width, height, 1, false).
WithScreenOrientation(&emulation.ScreenOrientation{
Type: emulation.OrientationTypePortraitPrimary,
Angle: 0,
}).
Do(ctx)
if err != nil {
return err
}
// capture screenshot
*res, err = page.CaptureScreenshot().
WithQuality(quality).
WithClip(&page.Viewport{
X: contentSize.X,
Y: contentSize.Y,
Width: contentSize.Width,
Height: contentSize.Height,
Scale: 1,
}).Do(ctx)
if err != nil {
return err
}
return nil
}),
}
}

Convert map[string][]string into a yaml structure

I try to convert a make(map[string]string) into a yaml like that:
Yaml Output desire:
items:
keys1:value1
keys2:value2
keys3:value3
keys4:value4
The keys,values are this listKey map of string. J = string = {"key1":"value1","key2":"value2" }
type Items struct {
items string
ItemsValues map[string][]string
}
func ConvertToYelm(j string){
y := Items{}
var dataJson map[string]string
err := json.Unmarshal([]byte(j), &dataJson)
if err != nil {
fmt.Println(err)
return
}
listKey := make(map[string]string)
for k := range dataJson{
listKey[k] = k
}
yelm, err := yaml.Marshal(listKey)
if err != nil {
fmt.Println(err)
return
}
err = yaml.Unmarshal(yelm, Items)
if err != nil {
fmt.Println(err)
return
}
yeml2, err := yaml.Marshal(&yelm)
fmt.Printf ("%s", string(yeml2))
To be honest, I'm a little bit lost here, thank you for the help
To get the exact YAML from your post:
items:
keys1:value1
keys2:value2
keys3:value3
keys4:value4
You can do this (Go Playground):
package main
import (
"fmt"
"gopkg.in/yaml.v2"
)
type ItemsStruct struct {
Items map[string]string `yaml:"items"`
}
func main() {
itms := &ItemsStruct{Items: map[string]string{
"keys1": "value1",
"keys2": "value2",
"keys3": "value3",
"keys4": "value4"}}
yamlBytes, err := yaml.Marshal(itms)
if err != nil {
//handle error
}
fmt.Println(string(yamlBytes))
}
And just to add, I see your code is decoding this JSON {"key1":"value1", "key2":"value2", ... } and then encoding it as YAML in your specified format. Here is the Go Playground for that.

Convert interface to its respecting map

For example if I have an interface{} value that originally a map[string]map[int64][]int64 or any other kind of map, how to get the key type of the map? or more precise, how to convert it to map[theKeyType]interface{}?
func Transverse(any interface{}) string {
res := ``
switch any.(type) {
case string:
return ``
case []byte:
return ``
case int, int64, int32:
return ``
case float32, float64:
return ``
case bool:
return ``
case map[int64]interface{}:
return ``
case map[string]interface{}:
return ``
case []interface{}:
return ``
default:
kind := reflect.TypeOf(any).Kind()
switch kind {
case reflect.Map:
// how to convert it to map[keyType]interface{} ?
}
return `` // handle other type
}
return ``
}
Getting the Key type is easy:
reflect.TypeOf(any).Key()
To make the entire conversion, you need to create a map value of type map[keyType]interface{} and then copy the values over. Below is a working example how this can be done:
package main
import (
"errors"
"fmt"
"reflect"
)
func InterfaceMap(i interface{}) (interface{}, error) {
// Get type
t := reflect.TypeOf(i)
switch t.Kind() {
case reflect.Map:
// Get the value of the provided map
v := reflect.ValueOf(i)
// The "only" way of making a reflect.Type with interface{}
it := reflect.TypeOf((*interface{})(nil)).Elem()
// Create the map of the specific type. Key type is t.Key(), and element type is it
m := reflect.MakeMap(reflect.MapOf(t.Key(), it))
// Copy values to new map
for _, mk := range v.MapKeys() {
m.SetMapIndex(mk, v.MapIndex(mk))
}
return m.Interface(), nil
}
return nil, errors.New("Unsupported type")
}
func main() {
foo := make(map[string]int)
foo["anisus"] = 42
bar, err := InterfaceMap(foo)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", bar.(map[string]interface{}))
}
Output:
map[string]interface {}{"anisus":42}
Playground: http://play.golang.org/p/tJTapGAs2b

How to get all addresses and masks from local interfaces in go?

How can I get all addresses and masks from local interfaces in golang?
I need the actual network mask configured along with every IP address.
This code does not show the network masks in Windows 7:
package main
import (
"fmt"
"log"
"net"
)
func localAddresses() {
ifaces, err := net.Interfaces()
if err != nil {
log.Print(fmt.Errorf("localAddresses: %v\n", err.Error()))
return
}
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
log.Print(fmt.Errorf("localAddresses: %v\n", err.Error()))
continue
}
for _, a := range addrs {
log.Printf("%v %v\n", i.Name, a)
}
}
}
func main() {
localAddresses()
}
UPDATE: This issue has been fixed in Go: https://github.com/golang/go/issues/5395
There are multiply types of addresses that a net.Interface might have. The Addr is just an interface which may contain a net.IPAddr. But with a type assertion or type switch you can access the actual address type:
package main
import (
"fmt"
"net"
)
func localAddresses() {
ifaces, err := net.Interfaces()
if err != nil {
fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
return
}
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
continue
}
for _, a := range addrs {
switch v := a.(type) {
case *net.IPAddr:
fmt.Printf("%v : %s (%s)\n", i.Name, v, v.IP.DefaultMask())
}
}
}
}
func main() {
localAddresses()
}
Edit
Unfortunately the net package doesn't return the Mask of the address. So, you will have to do the low level syscalls that the net package does. The code below is an example, but parsing of the ip and the mask still needs to be done:
package main
import (
"fmt"
"net"
"os"
"syscall"
"unsafe"
)
func getAdapterList() (*syscall.IpAdapterInfo, error) {
b := make([]byte, 1000)
l := uint32(len(b))
a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
// TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that
// contains IPv4 address list only. We should use another API
// for fetching IPv6 stuff from the kernel.
err := syscall.GetAdaptersInfo(a, &l)
if err == syscall.ERROR_BUFFER_OVERFLOW {
b = make([]byte, l)
a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
err = syscall.GetAdaptersInfo(a, &l)
}
if err != nil {
return nil, os.NewSyscallError("GetAdaptersInfo", err)
}
return a, nil
}
func localAddresses() error {
ifaces, err := net.Interfaces()
if err != nil {
return err
}
aList, err := getAdapterList()
if err != nil {
return err
}
for _, ifi := range ifaces {
for ai := aList; ai != nil; ai = ai.Next {
index := ai.Index
if ifi.Index == int(index) {
ipl := &ai.IpAddressList
for ; ipl != nil; ipl = ipl.Next {
fmt.Printf("%s: %s (%s)\n", ifi.Name, ipl.IpAddress, ipl.IpMask)
}
}
}
}
return err
}
func main() {
err := localAddresses()
if err != nil {
panic(err)
}
}
Some code shamelessly borrowed from interface_windows.go. Even comments are left intact
I'm modifying #ANisus answer and get all interfaces & masks (tested on Windows 10 & WSL in it (Microsoft Ubuntu 16.04.5 LTS):
package main
import (
"fmt"
"net"
)
func localAddresses() {
ifaces, err := net.Interfaces()
if err != nil {
fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
return
}
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
continue
}
for _, a := range addrs {
switch v := a.(type) {
case *net.IPAddr:
fmt.Printf("%v : %s (%s)\n", i.Name, v, v.IP.DefaultMask())
case *net.IPNet:
fmt.Printf("%v : %s [%v/%v]\n", i.Name, v, v.IP, v.Mask)
}
}
}
}
func main() {
localAddresses()
}
This should give you the ipnet you're looking for.
ip, ipnet, err := net.ParseCIDR(a.String())
I know this post for Windows 7, but if you use Mac OS X hope this could help you.
Just Call GetNetMask("en0")
func GetNetMask(deviceName string) string {
switch runtime.GOOS {
case "darwin":
cmd := exec.Command("ipconfig", "getoption", deviceName, "subnet_mask")
out, err := cmd.CombinedOutput()
if err != nil {
return ""
}
nm := strings.Replace(string(out), "\n", "", -1)
log.Println("netmask=", nm, " OS=", runtime.GOOS)
return nm
default:
return ""
}
return ""
}

Resources