繁体   English   中英

创建迭代JSON目录树-Golang

[英]Create iterative JSON directory tree - Golang

我在创建用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
}

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

主要问题是结构不是切片或映射之类的描述符值,也就是说,如果将结构值分配给变量,它将被复制。 如果将结构值分配给切片或数组的元素,则将复制切片。 他们将不会被链接!

因此,当您将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
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM