Is there a way to unmarshal XML tags with dynamic attributes (I don't know which attributes I'll get every time).
Maybe it's not supported yet. See Issue 3633: encoding/xml: support for collecting all attributes
Something like :
package main
import (
"encoding/xml"
"fmt"
)
func main() {
var v struct {
Attributes []xml.Attr `xml:",any"`
}
data := `<TAG ATTR1="VALUE1" ATTR2="VALUE2" />`
err := xml.Unmarshal([]byte(data), &v)
if err != nil {
panic(err)
}
fmt.Println(v)
}
As of late 2017, this is supported by using:
var v struct {
Attributes []xml.Attr `xml:",any,attr"`
}
Please see https://github.com/golang/go/issues/3633
You need to implement your own XMLUnmarshaler
package main
import (
"encoding/xml"
"fmt"
)
type CustomTag struct {
Name string
Attributes []xml.Attr
}
func (c *CustomTag) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
c.Name = start.Name.Local
c.Attributes = start.Attr
return d.Skip()
}
func main() {
v := &CustomTag{}
data := []byte(`<tag ATTR1="VALUE1" ATTR2="VALUE2" />`)
err := xml.Unmarshal(data, &v)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", v)
}
outputs
&{Name:tag Attributes:[{Name:{Space: Local:ATTR1} Value:VALUE1} {Name:{Space: Local:ATTR2} Value:VALUE2}]}
http://play.golang.org/p/9ZrpIT32o_
Related
Is there a way to list out all functions that uses/returns a specific type?
For example: I'm interested to use the following function.
func ListenAndServe(addr string, handler Handler) error
How can I find out all functions (across all Go packages) that can return a Handler?
I'd write an analysis tool using the x/tools/go/analysis framework. Here's a rough sketch that you can run on any module (it uses go/packages underneath so it fully supports modules):
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/token"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/singlechecker"
)
var RtAnalysis = &analysis.Analyzer{
Name: "rtanalysis",
Doc: "finds functions by return type",
Run: run,
}
func main() {
singlechecker.Main(RtAnalysis)
}
func run(pass *analysis.Pass) (interface{}, error) {
for _, file := range pass.Files {
ast.Inspect(file, func(n ast.Node) bool {
if funcTy, ok := n.(*ast.FuncType); ok {
if funcTy.Results != nil {
for _, fl := range funcTy.Results.List {
if tv, ok := pass.TypesInfo.Types[fl.Type]; ok {
if tv.Type.String() == "net/http.Handler" {
ns := nodeString(funcTy, pass.Fset)
fmt.Printf("%s has return of type net/http.Handler\n", ns)
}
}
}
}
}
return true
})
}
return nil, nil
}
// nodeString formats a syntax tree in the style of gofmt.
func nodeString(n ast.Node, fset *token.FileSet) string {
var buf bytes.Buffer
format.Node(&buf, fset, n)
return buf.String()
}
I'm about to manipulate XMLs (without schema) in Go and my goal is to achieve a simple framework without a hardwired internal representation of xml's structure.
I have a code like this:
package main
import (
"bytes"
"encoding/xml"
"io"
"io/ioutil"
"log"
"os"
)
var decoder *xml.Decoder
var encoder *xml.Encoder
type Tokens struct {
root xml.Token
children []Tokens
}
type Tokenizer interface {
toStr() string
}
func setTokens(t xml.Token) Tokens {
res := Tokens{}
res.root = t
res.children = make([]Tokens, 0)
for {
tok, err := decoder.Token()
if err == io.EOF {
break
}
t := setTokens(xml.CopyToken(tok))
res.children = append(res.children, t)
}
return res
}
func (t Tokens) toStr() {
encoder.EncodeToken(t.root)
for i := range t.children {
t.children[i].toStr()
}
}
func main() {
fc, err := ioutil.ReadFile("testXML.xml")
if err != nil {
log.Fatal(err)
return
}
out, err := os.Create("./testOut.xml")
decoder = xml.NewDecoder(bytes.NewReader(fc))
encoder = xml.NewEncoder(out)
t, err := decoder.Token()
if err == io.EOF {
return
}
tokens := setTokens(t)
tokens.toStr()
encoder.Encode(t)
}
An input xml like this:
<ParamSection SectVersion="1">
<Version>22</Version>
</ParamSection>
And an output like this:
<ParamSection SectVersion="1">
<Version>22</Version>
</ParamSection><StartElement><Name></Name><Attr><Name></Name><Value>1</Value></Attr></StartElement>
Which is more than nothing. There are two obvious problems:
istead of a \t (tabulator)
<StartElement><Name></Name><Attr><Name></Name><Value>1</Value></Attr></StartElement>
instead of nothing (Encoder seems to append the attribs of the first
field).
Any idea what is the problem?
I have a bunch of very similar structs (A and B in the example) whose instances I want to handle in some function ( f() in the example) and then insert them into my database. I figured I could handle that with the empty interface somehow, but it seems this is not the solution as I get the error:
i: &{{6 2019-04-03 15:11:37.822100431 +0200 CEST m=+0.001291882} 7} *main.A
2019/04/03 15:11:37 Insert i no table found for type:
exit status 1
I tried to create some minimal but executable example:
package main
import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
gorp "gopkg.in/gorp.v2"
"log"
"time"
)
type Meta struct {
Id int
CreatedAt time.Time
}
type A struct {
Meta
Value int
}
type B struct {
Meta
Value string
}
var dbmap *gorp.DbMap
func f(i interface{}) {
fmt.Printf("i: %v %T\n", i, i)
err := dbmap.Insert(&i)
checkErr(err, "Insert i")
}
func main() {
Init()
a := A{Meta: Meta{CreatedAt: time.Now()}, Value: 7}
b := B{Meta: Meta{CreatedAt: time.Now()}, Value: "seven"}
err := dbmap.Insert(&a) // works
checkErr(err, "Insert a")
err = dbmap.Insert(&b) // works
checkErr(err, "Insert b")
f(&a) // fails
}
func Init() {
db, err := sql.Open("sqlite3", "/tmp/post_db.bin")
checkErr(err, "sql.Open failed")
dbmap = &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
dbmap.AddTableWithName(A{}, "As").SetKeys(true, "Id")
dbmap.AddTableWithName(B{}, "Bs").SetKeys(true, "Id")
err = dbmap.CreateTablesIfNotExists()
checkErr(err, "Couldn't create tables")
}
func checkErr(err error, msg string) {
if err != nil {
log.Fatalln(msg, err)
}
}
What's the right way to do that? In C++ I'd simply use templates ;)
If you are calling you func like f(&a). You should call inside func f just dbmap.Insert(i), because your value is already a pointer. So your func will look like
func f(i interface{}) {
fmt.Printf("i: %v %T\n", i, i)
err := dbmap.Insert(i)
checkErr(err, "Insert i")
}
I have the following code in a golang plugin module:
plug.go
package main
import "fmt"
var (
Thing = New("first thing")
ThingFactory = thingFactory{}
)
type thing struct {
i int
s string
}
func New(s string) thing {
return thing{s: s}
}
func (t *thing) Say() string {
t.i++
return fmt.Sprintf("%s - %d", t.s, t.i)
}
type thingFactory struct{}
func (t thingFactory) Make(s string) thing {
return New(s)
}
it is compiled as a .so object and used in another program:
main.go
package main
import (
"fmt"
"plugin"
)
func main() {
p, err := plugin.Open("../plug/plug.so")
if err != nil {
panic(err)
}
symbol, err := p.Lookup("Thing")
if err != nil {
panic(err)
}
thing := symbol.(Sayer)
fmt.Println(thing.Say())
symbol, err = p.Lookup("ThingFactory") // <-problems start here
if err != nil {
panic(err)
}
factory := symbol.(GetSayer)
madeThing := factory.Make("how about me?")
fmt.Println(madeThing.Say())
fmt.Println(madeThing.Say())
}
type Sayer interface {
Say() string
}
type GetSayer interface {
Make(string) Sayer
}
I'm able to lookup the Thing, and call Say() on it, but the second interface conversion panics:
first thing - 1
panic: interface conversion: *main.thingFactory is not main.GetSayer: missing method Make
even though the runtime recognizes the first symbol as a Sayer it doesn't recognize that thingFactory obviously has a Make() method, which should return something that is also a Sayer.
Am I missing something obvious here?
The first problem is that in your plugin thingFactory (more precicely *thingfactory) does not have a method described in your main app's GetSayer interface:
Make(string) Sayer
You have:
Make(string) thing
So (first) you have to change thingFactory.Make() to this:
type Sayer interface {
Say() string
}
func (t thingFactory) Make(s string) Sayer {
th := New(s)
return &th
}
After this it still won't work. And the reason for this is because the plugin's Sayer type is not identical to your main app's Sayer type. But they must be the same in order to implement your main app's GetSayer interface.
One solution is to "outsource" the Sayer interface to its own package, and use this common, shared package both in the plugin and in the main app.
Let's create a new package, call it subplay:
package subplay
type Sayer interface {
Say() string
}
Import this package and use it in the plugin:
package main
import (
"fmt"
"path/to/subplay"
)
var (
Thing = New("first thing")
ThingFactory = thingFactory{}
)
type thing struct {
i int
s string
}
func New(s string) thing {
return thing{s: s}
}
func (t *thing) Say() string {
t.i++
return fmt.Sprintf("%s - %d", t.s, t.i)
}
type thingFactory struct{}
func (t thingFactory) Make(s string) subplay.Sayer {
th := New(s)
return &th
}
And also import and use it in the main app:
package main
import (
"fmt"
"path/to/subplay"
"plugin"
)
func main() {
p, err := plugin.Open("../plug/plug.so")
if err != nil {
panic(err)
}
symbol, err := p.Lookup("Thing")
if err != nil {
panic(err)
}
thing := symbol.(subplay.Sayer)
fmt.Println(thing.Say())
symbol, err = p.Lookup("ThingFactory")
if err != nil {
panic(err)
}
factory := symbol.(GetSayer)
madeThing := factory.Make("how about me?")
fmt.Println(madeThing.Say())
fmt.Println(madeThing.Say())
}
type GetSayer interface {
Make(string) subplay.Sayer
}
Now it will work, and output will be:
first thing - 1
how about me? - 1
how about me? - 2
See related questions:
go 1.8 plugin use custom interface
How do Go plugin dependencies work?
Your plugin Make method should return a Sayer object not thing
type Sayer interface {
Say() string
}
func (t *thingFactory) Make(s string) Sayer {
return New(s)
}
Can you see why json values dont get saved:
Update: And if you would like to explain why this is downgraded as "Off topic"?
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type UsedInContext struct {
UsedIn string `json:"usedin"`
ContextName string `json:"contextname"`
}
type ContextData struct {
usedInContext []UsedInContext `json:"usedincontext"`
}
func saveData() {
var jsonBlob = []byte(`{
"usedincontext":
[
{"usedin":"local", "contextname":"desktop"}
]
}`)
usedInContext := UsedInContext{}
err := json.Unmarshal(jsonBlob, &usedInContext)
if err != nil {
}
usedInContextJson, _ := json.Marshal(usedInContext)
err = ioutil.WriteFile("data.json", usedInContextJson, 0644)
fmt.Printf("%+v", usedInContext)
}
I get the following:
{"usedin":"","contextname":""}
You unmarshal your document to the type UsedInContext, while it matches the schema for ContextData:
type ContextData struct {
UsedInContext []UsedInContext `json:"usedincontext"` // exported
}
var data ContextData
json.Unmarshal(jsonBlob, &data)
fmt.Printf("%+v", data)