使用Gson序列化apache-commons-io获取的目录文件树仅返回部分内容
解决Gson序列化File列表仅返回部分内容的问题
这个问题我之前也碰到过,核心原因是File对象存在循环引用(比如子目录的getParentFile()会指向父目录,父目录又包含子目录),Gson默认序列化时遇到循环引用会直接停止,所以只能得到部分内容。另外直接序列化File对象会包含很多我们不需要的内部属性,也容易出问题。
下面是完整的解决方案,分三步实现:
1. 定义自定义的目录结构DTO
创建一个只包含我们需要信息的实体类,避免循环引用和冗余字段:
import java.util.ArrayList; import java.util.List; import java.io.File; public class FileNode { private String absolutePath; private boolean isDirectory; private List<FileNode> children; public FileNode(File file) { this.absolutePath = file.getAbsolutePath(); this.isDirectory = file.isDirectory(); this.children = new ArrayList<>(); } // Getter和Setter方法,或者用Lombok的@Data注解简化 public String getAbsolutePath() { return absolutePath; } public void setAbsolutePath(String absolutePath) { this.absolutePath = absolutePath; } public boolean isDirectory() { return isDirectory; } public void setDirectory(boolean directory) { isDirectory = directory; } public List<FileNode> getChildren() { return children; } public void setChildren(List<FileNode> children) { this.children = children; } }
2. 将扁平的File列表转换为树形结构
FileUtils.listFilesAndDirs返回的是扁平列表,我们需要把它转换成树形结构,这样既符合目录的层级逻辑,又能避免循环引用:
import java.util.List; import java.util.Map; import java.util.HashMap; import android.os.Environment; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.DirectoryFileFilter; public class DirectoryTreeBuilder { public static FileNode buildDirectoryTree() { // 获取外部存储根目录的所有文件和子目录 List<File> flatFileList = (List<File>) FileUtils.listFilesAndDirs( Environment.getExternalStorageDirectory(), DirectoryFileFilter.INSTANCE, DirectoryFileFilter.INSTANCE ); File rootDir = Environment.getExternalStorageDirectory(); FileNode rootNode = new FileNode(rootDir); // 用Map快速映射File到对应的FileNode,方便查找父节点 Map<File, FileNode> fileNodeMap = new HashMap<>(); fileNodeMap.put(rootDir, rootNode); // 遍历所有文件/目录,构建树形结构 for (File file : flatFileList) { if (file.equals(rootDir)) continue; FileNode currentNode = new FileNode(file); fileNodeMap.put(file, currentNode); // 找到父节点并添加当前节点到子列表 File parentFile = file.getParentFile(); FileNode parentNode = fileNodeMap.get(parentFile); if (parentNode != null) { parentNode.getChildren().add(currentNode); } } return rootNode; } }
3. 用Gson序列化树形结构
现在序列化自定义的FileNode对象就不会有循环引用问题了,能得到完整的目录结构JSON:
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; public class JsonSerializer { public static void serializeDirectoryTreeToJson() { FileNode rootNode = DirectoryTreeBuilder.buildDirectoryTree(); // 初始化Gson,可选开启格式化输出 Gson gson = new GsonBuilder() .setPrettyPrinting() .create(); // 转换为JSON字符串 String directoryJson = gson.toJson(rootNode); // 保存到JSON文件 File outputFile = new File(Environment.getExternalStorageDirectory(), "directory_structure.json"); try { FileUtils.writeStringToFile(outputFile, directoryJson, StandardCharsets.UTF_8); System.out.println("JSON文件已成功生成:" + outputFile.getAbsolutePath()); } catch (IOException e) { e.printStackTrace(); } } }
关键注意事项
- 权限问题:Android 6.0及以上需要动态申请
READ_EXTERNAL_STORAGE权限;Android 10及以上如果需要访问所有文件,需要申请MANAGE_EXTERNAL_STORAGE权限,或者改用MediaStore API。 - 性能优化:如果目录结构非常庞大,构建树形结构可能会占用较多内存,建议分批处理或者按需加载子目录。
- 避免循环引用:自定义DTO只保留必要的路径、是否为目录、子节点列表,完全切断了File对象的循环引用链,这是解决序列化不完整的核心。
内容的提问来源于stack exchange,提问作者moictab
相关产品推荐
相关产品推荐

