Obtenga una representación de cadena simple del tipo de un campo de estructura
Frecuentes
Visto 5,524 veces
16
Using Go’s ast package, I am looping over a struct’s field list like so:
type Thing struct {
Field1 string
Field2 []int
Field3 map[byte]float64
}
// typ is a *ast.StructType representing the above
for _, fld := range typ.Fields.List {
// get fld.Type as string
}
…and would like to get a simple string representation of fld.Type
, as it appears in the source code, e.g. []int
or map[byte]float64
.
The ast package Tipo de campo Type property is an Expr
, so I’ve found myself getting off into the weeds using type switches and handling every type specifically – when my only goal is to get out the plain string to the right of each field name, which seems like it should be simpler.
¿Existe una forma sencilla?
4 Respuestas
20
There are two things you could be getting at here, one is the tipo of an expression as would ultimately be resolved during compilation and the other is the código which would determine that type.
Digging through the docs, I don't believe the first is at all available. You can get at the later, however, by using End()
y Pos()
on Node
.
Quick example program:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `
package foo
type Thing struct {
Field1 string
Field2 []int
Field3 map[byte]float64
}`
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
panic(err)
}
// hard coding looking these up
typeDecl := f.Decls[0].(*ast.GenDecl)
structDecl := typeDecl.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType)
fields := structDecl.Fields.List
for _, field := range fields {
typeExpr := field.Type
start := typeExpr.Pos() - 1
end := typeExpr.End() - 1
// grab it in source
typeInSource := src[start:end]
fmt.Println(typeInSource)
}
}
Esto imprime:
string
[]int
map[byte]float64
I through this together in the golang playground, if you want to mess with it.
respondido 27 nov., 13:06
Helpful and also unfortunate. :) The file business is way upstream in my program, gonna have to restructure. - mate sherman
6
Puedes usar go/types
ExprString
This works with complicated types like []string
, []map[string]string
, etc.
import (
...
"go/types"
...
)
...
// typ is a *ast.StructType representing the above
for _, fld := range typ.Fields.List {
...
typeExpr := fld.Type
typeString := types.ExprString(typeExpr)
...
}
Respondido el 24 de diciembre de 20 a las 03:12
This is a better answer, go/ast
stores the Type in the Expr
, Utilizar types.ExprString
para conseguirlo. - Yami Odimel
5
Esto es exactamente lo que Fprint en el capítulo respecto a la go/printer
package is for. It takes any AST node as an argument and writes its string representation to a io.Writer
.
You can use it in your example as follows:
package main
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"log"
)
func main() {
src := `
package foo
type Thing struct {
Field1 string
Field2 []int
Field3 map[byte]float64
}`
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
panic(err)
}
typeDecl := f.Decls[0].(*ast.GenDecl)
structDecl := typeDecl.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType)
for i, fld := range structDecl.Fields.List {
// get fld.Type as string
var typeNameBuf bytes.Buffer
err := printer.Fprint(&typeNameBuf, fset, fld.Type)
if err != nil {
log.Fatalf("failed printing %s", err)
}
fmt.Printf("field %d has type %q\n", i, typeNameBuf.String())
}
}
Salida:
field 0 has type "string"
field 1 has type "[]int"
field 2 has type "map[byte]float64"
Try it in playground: https://play.golang.org/p/cyrCLt_JEzQ
Respondido 27 Feb 20, 22:02
2
I found a way to do this without using the original source code as a reference for simple members (not slices, arrays or structs):
for _, field := range fields {
switch field.Type.(type) {
case *ast.Ident:
stype := field.Type.(*ast.Ident).Name // The type as a string
tag = ""
if field.Tag != nil {
tag = field.Tag.Value //the tag as a string
}
name := field.Names[0].Name //name as a string
...
For the non-simple members you just need another case statement (IE: case *ast.ArrayType:
).
Respondido 25 Feb 18, 01:02
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas go abstract-syntax-tree or haz tu propia pregunta.
¿Has probado
theFileString[fld.Type.Pos():fld.Type.End()]
? - mjibson