创建迭代JSON目录树 - Golang

时间:2015-10-06 05:08:51

标签: go tree iteration directory-structure

我在创建一个我在GoLang中递归编写的程序的迭代版本时遇到了麻烦。目标是获取目录路径并返回包含该目录中的文件信息的JSON树,并保留目录结构。以下是我到目前为止的情况:

我创建了一个File结构,它将包含目录树中每个条目的信息:

type File struct {
    ModifiedTime time.Time `json:"ModifiedTime"`
    IsLink       bool      `json:"IsLink"`
    IsDir        bool      `json:"IsDir"`
    LinksTo      string    `json:"LinksTo"`
    Size         int64     `json:"Size"`
    Name         string    `json:"Name"`
    Path         string    `json:"Path"`
    Children     []File    `json:"Children"`
}

在我的迭代程序中,我创建了一个堆栈来模拟递归调用。

func iterateJSON(path string) {
    var stack []File
    var child File
    var file File
    rootOSFile, _ := os.Stat(path)
    rootFile := toFile(rootOSFile, path) //start with root file
    stack = append(stack, rootFile) //append root to stack 
    for len(stack) > 0 { //until stack is empty,
        file = stack[len(stack)-1] //pop entry from stack
        stack = stack[:len(stack)-1] 
        children, _ := ioutil.ReadDir(file.Path) //get the children of entry 
        for i := 0; i < len(children); i++ { //for each child
            child = (toFile(children[i], path+"/"+children[i].Name())) //turn it into a File object
            file.Children = append(file.Children, child) //append it to the children of the current file popped
            stack = append(stack, child) //append the child to the stack, so the same process can be run again
        }
    }
    rootFile.Children
    output, _ := json.MarshalIndent(rootFile, "", "     ")
    fmt.Println(string(output))
}

func toFile(file os.FileInfo, path string) File {
    var isLink bool
    var linksTo string
    if file.Mode()&os.ModeSymlink == os.ModeSymlink {
        isLink = true
        linksTo, _ = filepath.EvalSymlinks(path + "/" + file.Name())
    } else {
        isLink = false
        linksTo = ""
    }
    JSONFile := File{ModifiedTime: file.ModTime(),
        IsDir:    file.IsDir(),
        IsLink:   isLink,
        LinksTo:  linksTo,
        Size:     file.Size(),
        Name:     file.Name(),
        Path:     path,
        Children: []File{}}
    return JSONFile
}

理论上,当我们在堆栈中移动时,子文件应该附加到根文件。但是,唯一返回的是根文件(不附加任何子项)。知道为什么会这样吗?

1 个答案:

答案 0 :(得分:3)

主要问题是结构体不是像切片或贴图这样的描述符值,也就是说,如果将一个struct值赋给变量,它将被复制。如果将struct值分配给切片或数组的元素,则将复制切片。他们不会联系!

因此,当您将rootFile添加到stack时,然后弹出stack中的元素(等于rootFile)并修改弹出的元素元素,您将不会观察到局部变量rootFile中的更改。

解决方案很简单:使用指向结构的指针。

您的代码中也有错误:

child = (toFile(children[i], path+"/"+children[i].Name())) //turn it into a File object

应该是:

child = (toFile(children[i], file.Path+"/"+children[i].Name())) // ...

改进代码的提示:

我宁愿使用path.Join()filepath.Join()来加入路径元素:

child = toFile(children[i], filepath.Join(file.Path, children[i].Name()))

如果初始路径以斜杠或反斜杠结尾,并且您明确地将其与另一个斜杠连接,则您的代码甚至可能无效。 Join()会照顾这些,所以你不必这样做。

不要在函数开头声明所有局部变量,只有在需要它们时,才能在最内部的块中声明它们。这将确保您不会意外地分配给错误的变量,并且您将知道它不会在最里面的块之外进行修改(因为它不在范围内) - 这有助于更容易地理解您的代码。您也可以使用short variable declaration

使用for ... range构造,更清洁。例如:

for _, chld := range children {
    child := toFile(chld, filepath.Join(file.Path, chld.Name()))
    file.Children = append(file.Children, child)
    stack = append(stack, child)
}

还可以使用zero values,例如,如果文件不是链接,则无需设置IsLinkLinksTo字段,因为零值为{{ 1}}和false这就是你最终会得到的结果。

尽管在这里可能并不重要,但总是处理错误,打印或记录它们至少这样你就不会浪费时间去弄清楚如果不符合预期会出现什么问题(你最终会在你的代码中搜索bug,几小时后你最终添加了打印错误,看到错误不在你的代码中,而是在其他地方)。

使用上述指针和提示的工作变体

""