创建可迭代的JSON目录树 - Golang

3

我正在尝试使用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个回答

6
主要问题在于结构体不像切片(slice)或映射(map)那样是描述符(descriptor)值,也就是说,如果你将一个结构体的值赋给一个变量,它会被复制。如果你将一个结构体的值赋值给一个切片(slice)或数组(array)的元素,那么这个切片(slice)也会被复制。它们之间不会相互关联!
因此,当您将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()可以解决这些问题,因此您不必担心。
不要在函数开头声明所有本地变量,只有在需要时才声明,在最内部需要它们的块中声明。这将确保您不会意外地赋值给错误的变量,并且您将知道它没有在最内部的块之外修改(因为在其外部它不在范围内)- 这有助于更轻松地理解您的代码。您还可以使用短变量声明
充分利用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)
}

此外,还要利用零值,例如如果一个文件不是链接,那么您无需设置IsLinkLinksTo字段,因为零值是false"",这就是您最终将得到的值。
虽然这里可能并不重要,但请始终处理错误,至少打印或记录它们,以便如果某些内容不符合预期,您不会浪费时间查找问题(否则您将不得不搜索代码中的错误,几个小时后才能添加打印错误并发现错误并不在您的代码中,而是在其他地方)。
使用指针和上述技巧的工作变体。
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) {
    rootOSFile, _ := os.Stat(path)
    rootFile := toFile(rootOSFile, path) //start with root file
    stack := []*File{rootFile}

    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 _, chld := range children {          //for each child
            child := toFile(chld, filepath.Join(file.Path, chld.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
        }
    }

    output, _ := json.MarshalIndent(rootFile, "", "     ")
    fmt.Println(string(output))
}

func toFile(file os.FileInfo, path string) *File {
    JSONFile := File{ModifiedTime: file.ModTime(),
        IsDir:    file.IsDir(),
        Size:     file.Size(),
        Name:     file.Name(),
        Path:     path,
        Children: []*File{},
    }
    if file.Mode()&os.ModeSymlink == os.ModeSymlink {
        JSONFile.IsLink = true
        JSONFile.LinksTo, _ = filepath.EvalSymlinks(filepath.Join(path, file.Name()))
    } // Else case is the zero values of the fields
    return &JSONFile
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接