获取struct字段类型的简单字符串表示形式

时间:2013-11-27 05:07:27

标签: go abstract-syntax-tree

使用Go的ast package,我循环遍历struct的字段列表,如下所示:

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
}

...并希望获得fld.Type的简单字符串表示,因为它出现在源代码中,例如“[] int”或“map [byte] float64”。

ast包field type Type属性是一个Expr,所以我发现自己使用类型开关进入杂草并专门处理每种类型 - 当我唯一的目标是将纯字符串输出到每个字段名称的右侧,看起来应该更简单。

有简单的方法吗?

3 个答案:

答案 0 :(得分:14)

你可以在这里找到两件事,一个是表达式的类型,最终会在编译时被解析,另一个是代码确定那种类型。

通过文档挖掘,我不相信第一个是可用的。不过,您可以在Node上使用End()Pos()来获取。

快速示例程序:

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)
    }

}

打印:

string
[]int
map[byte]float64

我在golang playground中一起讨论这个问题,如果你想搞砸它。

答案 1 :(得分:2)

我找到了一种方法来做到这一点,而不使用原始源代码作为简单成员(不是切片,数组或结构)的参考:

          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
                     ...

对于非简单成员,您只需要另一个案例陈述(IE:case *ast.ArrayType:)。

答案 2 :(得分:1)

我发现最好的方法是使用Fprint method in the go/printer package

它接受任何AST节点作为参数,并将其字符串表示形式写到任何io.Writer中。

您应该可以在示例中使用它,如下所示:

for i, fld := range typ.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 name %q", i, typeNameBuf.String())
}