草庐IT

go - 递归结构反射错误 : panic: reflect: Field of non-struct type

coder 2023-06-30 原文

尝试创建一个递归遍历结构的函数,并根据特定标记修改任何字符串字段。

使用反射非常乏味。第一次使用它,遇到了一些麻烦。

我的一行代码引起了 panic :

panic: reflect: Field of non-struct type

panic 来自这一行:

tf := vf.Type().Field(i)

我正在尝试获取类型字段,以便从中获取标签。

这里是完整的函数:

func Sanitize(s interface{}) error {
    v := reflect.ValueOf(s)

    // It's a pointer struct, convert to the value that it points to.
    if v.Kind() == reflect.Ptr && !v.IsNil() {
        v = v.Elem()
    }

    // Not a struct, return an error.
    if v.Kind() != reflect.Struct {
        return &InvalidSanitizerError{Type: reflect.TypeOf(s)}
    }

    for i := 0; i < v.NumField(); i++ {
        vf := v.Field(i)

        if vf.Kind() == reflect.Struct {
            // Recurse.
            err := Sanitize(v.Field(i).Interface())
            if err != nil {
                return err
            }

            // Move onto the next field.
            continue
        }


        if vf.Kind() == reflect.String {
            tf := vf.Type().Field(i) // <-- TROUBLE MAKER

            // Get the field tag value
            tag := tf.Tag.Get("sanitize")

            // Skip if tag is not defined or ignored
            if tag == "" || tag == "-" {
                continue
            }
            shouldSanitize, err := strconv.ParseBool(tag)
            if err != nil {
                return err
            }

            if shouldSanitize && vf.CanSet() {
                vf.SetString(policy.Sanitize(vf.String()))
            }
        }
    }

    return nil
}

这是应该如何使用函数的示例:

type User struct {
    Name string `sanitize:"true"`
    Bio *Bio
}

type Bio struct {
    Text string `sanitize:"true"`
}

func main() {
    user := &User{
        Name: "Lansana<script>alert('rekt');</script>",
        Bio: &Bio{
            Text: "Hello world</script>alert('rekt');</script>",
        },
    }
    if err := Sanitize(user); err != nil {
        panic(err)
    }

    fmt.Println(user.Name) // Lansana
    fmt.Println(user.Bio.Text) // Hello world
}

任何关于 panic 的见解都将不胜感激。

最佳答案

在为此工作了一整天之后,这就是我最终想出的解决方案,它可以递归地遍历结构并修改具有特定标记的所有字符串值的值。

Sanitize 函数只允许指向结构的指针,但嵌套结构可以是指针或值;没关系。

我问题中的示例将与下面的函数一起使用,并且它通过了我的所有测试。

func Sanitize(s interface{}) error {
    if s == nil {
        return nil
    }

    val := reflect.ValueOf(s)

    // If it's an interface or a pointer, unwrap it.
    if val.Kind() == reflect.Ptr && val.Elem().Kind() == reflect.Struct {
        val = val.Elem()
    } else {
        return &InvalidStructError{message: "s must be a struct"}
    }

    valNumFields := val.NumField()

    for i := 0; i < valNumFields; i++ {
        field := val.Field(i)
        fieldKind := field.Kind()

        // Check if it's a pointer to a struct.
        if fieldKind == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
            if field.CanInterface() {
                // Recurse using an interface of the field.
                err := Sanitize(field.Interface())
                if err != nil {
                    return err
                }
            }

            // Move onto the next field.
            continue
        }

        // Check if it's a struct value.
        if fieldKind == reflect.Struct {
            if field.CanAddr() && field.Addr().CanInterface() {
                // Recurse using an interface of the pointer value of the field.
                err := Sanitize(field.Addr().Interface())
                if err != nil {
                    return err
                }
            }

            // Move onto the next field.
            continue
        }

        // Check if it's a string or a pointer to a string.
        if fieldKind == reflect.String || (fieldKind == reflect.Ptr && field.Elem().Kind() == reflect.String) {
            typeField := val.Type().Field(i)

            // Get the field tag value.
            tag := typeField.Tag.Get("sanitize")

            // Skip if tag is not defined or ignored.
            if tag == "" || tag == "-" {
                continue
            }

            // Check if the tag is allowed.
            if tag != "true" && tag != "false" {
                return &InvalidTagError{message: "tag must be either 'true' or 'false'."}
            }

            // Parse it to a bool.
            shouldSanitize, err := strconv.ParseBool(tag)
            if err != nil {
                return err
            } else if !shouldSanitize {
                continue
            }

            // Set the string value to the sanitized string if it's allowed.
            // It should always be allowed at this point.
            if field.CanSet() {
                field.SetString(policy.Sanitize(field.String()))
            }

            continue
        }
    }

    return nil
}

关于go - 递归结构反射错误 : panic: reflect: Field of non-struct type,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49460380/

有关go - 递归结构反射错误 : panic: reflect: Field of non-struct type的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  3. ruby-on-rails - 一般建议和推荐的文件夹结构 - Sinatra - 2

    您将如何构建一个简单的Sinatra应用程序?我正在制作,我希望该应用具有以下功能:“应用程序”更像是一个包含所有信息的管理仪表板。然后另一个应用程序将通过REST访问信息。我还没有创建仪表板,只是从数据库中获取东西session和身份验证(尚未实现)您可以上传图片,其他应用可以显示这些图片我已经使用RSpec创建了一个测试文件通过Prawn生成报告目前的设置是这样的:app.rbtest_app.rb因为我实际上只有应用程序和测试文件。到目前为止,我已经将Datamapper用于ORM,将SQLite用于数据库。这是我的第一个Ruby/Sinatra项目,所以欢迎任何和所有建议-我应

  4. ruby - 如何在 ruby​​ 中复制目录结构,不包括某些文件扩展名 - 2

    我想编写一个ruby​​脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"

  5. ruby - 递归地将所有数字字符串转换为 Ruby 哈希中的整数 - 2

    我有一个随机大小的散列,它可能有类似"100"的值,我想将其转换为整数。我知道我可以使用value.to_iifvalue.to_i.to_s==value来做到这一点,但我不确定我将如何在我的散列中递归地做到这一点,考虑到一个值可以是一个字符串,或一个数组(哈希或字符串),或另一个哈希。 最佳答案 这是一个非常简单的递归实现(尽管必须同时处理数组和散列会增加一些技巧)。deffixnumifyobjifobj.respond_to?:to_i#IfwecancastittoaFixnum,doit.obj.to_ielsifobj

  6. Ruby:标准递归模式 - 2

    我经常迷上ruby​​的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情

  7. ruby - 为什么我用递归得到 "stack level too deep"? - 2

    我有这个ruby代码:defget_sumnreturn0ifn似乎正在为999之前的值工作。当我尝试9999时,它给了我这个:stackleveltoodeep(SystemStackError)所以,我添加了这个:RubyVM::InstructionSequence.compile_option={:tailcall_optimization=>true,:trace_instruction=>false}但什么也没发生。我的ruby版本是:ruby1.9.3p392(2013-02-22revision39386)[x86_64-darwin12.2.1]我还增加了机器的堆栈大

  8. ruby-on-rails - 在 Rails 中存储(结构化)配置数据的位置 - 2

    对于我正在编写的Rails3应用程序,我正在考虑从本地文件系统上的XML、YAML或JSON文件中读取一些配置数据。重点是:我应该把这些文件放在哪里?Rails应用程序中是否有用于存储此类内容的默认位置?附带说明一下,我的应用程序部署在Heroku上。 最佳答案 我经常做的是:如果文件是通用配置文件:我在目录/config中创建一个YAML文件,每个环境有一个上层key如果我为每个环境(大项目)创建一个文件:我为每个环境创建一个YAML并将它们存储在/config/environments/然后我在加载YAML的地方创建了一个初始化

  9. ruby-on-rails -/usr/local/lib/libz.1.dylib,文件是为 i386 构建的,它不是被链接的体系结构 (x86_64) - 2

    在我的mac上安装几个东西时遇到这个问题,我认为这个问题来自将我的豹子升级到雪豹。我认为这个问题也与macports有关。/usr/local/lib/libz.1.dylib,filewasbuiltfori386whichisnotthearchitecturebeinglinked(x86_64)有什么想法吗?更新更具体地说,这发生在安装nokogirigem时日志看起来像:xslt_stylesheet.c:127:warning:passingargument1of‘Nokogiri_wrap_xml_document’withdifferentwidthduetoproto

  10. ruby - 构建网络蜘蛛时,应该使用递归吗? - 2

    构建一个深度优先的网络蜘蛛,这意味着它将访问第一页上的所有链接,然后转到每个链接,并访问所有第二页上的链接...你应该使用递归吗?我发现这是CPU密集型的。defrecursion()linkz_on_first_page.eachdo|link|recursion(link)endendrecursion(firstpage) 最佳答案 绝对不是,由于万维网的实际性质,您很快就会遇到问题。当您访问带有主导航部分的网站时,每个页面都链接到其他页面,您就进入了一个无限循环。您可以跟踪您处理了哪些链接,但即便如此,递归循环并不真正适合万

随机推荐