Golang中如何支持Jinja2Schema
Golang中如何支持Jinja2Schema 是否有适用于Go的类似jinja2schema(https://jinja2schema.readthedocs.io/en/latest/)的库? 它包含一些实用工具,例如识别变量类型(推断方法),如标量、数组或对象?
4 回复
我正在寻找一个能够处理Jinja模板的库,它可以识别变量类型,例如是数组、对象还是标量值。
引用自 Jinja 官网:
Jinja 是一种现代且对设计师友好的模板语言,专为 Python 设计。
我怀疑在 Go 中对这种 Python 模板语言的支持会很少,尤其是在推断类型这种特殊用途方面。
在Go语言中,虽然没有直接对应jinja2schema的库,但可以通过标准库和第三方库实现类似功能。以下是一个使用text/template解析和类型推断的示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"strings"
"text/template"
"text/template/parse"
)
// 类型推断结构
type VariableType struct {
Name string
Type string
Children map[string]VariableType
}
func analyzeTemplate(tmplStr string) (map[string]VariableType, error) {
tmpl, err := template.New("test").Parse(tmplStr)
if err != nil {
return nil, err
}
result := make(map[string]VariableType)
// 遍历模板节点
var walkNodes func([]parse.Node)
walkNodes = func(nodes []parse.Node) {
for _, node := range nodes {
switch n := node.(type) {
case *parse.ActionNode:
// 解析变量使用
if len(n.Pipe.Cmds) > 0 {
for _, arg := range n.Pipe.Cmds[0].Args {
if field, ok := arg.(*parse.FieldNode); ok {
inferTypeFromPath(field.Ident, result)
}
}
}
case *parse.IfNode:
walkNodes(n.BranchNode.List.Nodes)
walkNodes(n.BranchNode.ElseList.Nodes)
case *parse.RangeNode:
walkNodes(n.BranchNode.List.Nodes)
walkNodes(n.BranchNode.ElseList.Nodes)
case *parse.WithNode:
walkNodes(n.BranchNode.List.Nodes)
walkNodes(n.BranchNode.ElseList.Nodes)
}
}
}
walkNodes(tmpl.Root.Nodes)
return result, nil
}
func inferTypeFromPath(ident []string, result map[string]VariableType) {
if len(ident) == 0 {
return
}
current := result
for i, part := range ident {
if i == len(ident)-1 {
// 叶子节点,推断为标量
current[part] = VariableType{
Name: part,
Type: "scalar",
}
} else {
// 中间节点,推断为对象
if _, exists := current[part]; !exists {
current[part] = VariableType{
Name: part,
Type: "object",
Children: make(map[string]VariableType),
}
}
current = current[part].Children
}
}
}
// 使用AST解析进行更精确的类型推断
func inferTypeFromGoCode(code, varName string) (string, error) {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, "", code, parser.AllErrors)
if err != nil {
return "", err
}
var foundType string
ast.Inspect(node, func(n ast.Node) bool {
if decl, ok := n.(*ast.GenDecl); ok {
for _, spec := range decl.Specs {
if valueSpec, ok := spec.(*ast.ValueSpec); ok {
for i, name := range valueSpec.Names {
if name.Name == varName && i < len(valueSpec.Values) {
switch expr := valueSpec.Values[i].(type) {
case *ast.BasicLit:
foundType = "scalar"
case *ast.CompositeLit:
switch expr.Type.(type) {
case *ast.ArrayType:
foundType = "array"
case *ast.MapType:
foundType = "object"
case *ast.StructType:
foundType = "object"
}
}
}
}
}
}
}
return true
})
return foundType, nil
}
func main() {
// 示例1:模板分析
tmpl := `{{.User.Name}} is {{.User.Age}} years old.
{{range .Items}}{{.}}{{end}}
{{with .Config}}{{.Setting}}{{end}}`
vars, _ := analyzeTemplate(tmpl)
fmt.Printf("模板变量分析:\n")
for k, v := range vars {
fmt.Printf("%s: %s\n", k, v.Type)
}
// 示例2:Go代码类型推断
code := `package main
var person = "John"
var scores = []int{90, 85, 95}
var config = map[string]string{"key": "value"}`
types := []string{"person", "scores", "config"}
for _, t := range types {
if inferred, err := inferTypeFromGoCode(code, t); err == nil {
fmt.Printf("%s: %s\n", t, inferred)
}
}
}
对于更复杂的模板分析,可以考虑以下第三方库:
- 模板解析增强:
import "github.com/valyala/fasttemplate"
// 快速模板分析示例
func analyzeFastTemplate(tmpl string) {
t, _ := fasttemplate.NewTemplate(tmpl, "{{", "}}")
var variables []string
t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) {
variables = append(variables, strings.TrimSpace(tag))
return 0, nil
})
fmt.Println("检测到的变量:", variables)
}
- JSON Schema生成(配合模板变量):
import "github.com/invopop/jsonschema"
func generateSchemaFromVars(vars map[string]string) *jsonschema.Schema {
schema := &jsonschema.Schema{
Type: "object",
Properties: make(map[string]*jsonschema.Schema),
}
for name, varType := range vars {
switch varType {
case "scalar":
schema.Properties[name] = &jsonschema.Schema{Type: "string"}
case "array":
schema.Properties[name] = &jsonschema.Schema{
Type: "array",
Items: &jsonschema.Schema{Type: "string"},
}
case "object":
schema.Properties[name] = &jsonschema.Schema{
Type: "object",
Properties: make(map[string]*jsonschema.Schema),
}
}
}
return schema
}
- 完整模板分析工具:
type TemplateAnalyzer struct {
Variables map[string]VariableInfo
}
type VariableInfo struct {
Path []string
InferredType string
UsageCount int
}
func (ta *TemplateAnalyzer) Analyze(tmplText string) error {
tmpl, err := template.New("").Parse(tmplText)
if err != nil {
return err
}
ta.Variables = make(map[string]VariableInfo)
// 收集所有变量引用
ast.Walk(tmpl.Tree.Root, func(n parse.Node) bool {
if field, ok := n.(*parse.FieldNode); ok {
key := strings.Join(field.Ident, ".")
info := ta.Variables[key]
info.Path = field.Ident
info.UsageCount++
// 基于使用模式推断类型
if len(field.Ident) > 1 {
info.InferredType = "object"
} else if strings.Contains(tmplText, "range ."+key) {
info.InferredType = "array"
} else {
info.InferredType = "scalar"
}
ta.Variables[key] = info
}
return true
})
return nil
}
这些方法提供了:
- 模板变量提取和路径分析
- 基于使用模式的类型推断(标量/数组/对象)
- 与Go类型系统的集成
- JSON Schema生成能力
虽然没有直接等价于jinja2schema的库,但通过组合text/template解析、AST分析和第三方JSON Schema库,可以在Go中实现类似的模板分析和类型推断功能。


