使用Gson库获取文件树

时间:2018-05-06 12:09:19

标签: java json file gson

我试图获取一个带有目录结构的JSON文件,包括递归的文件和子目录。

使用apache-commons-io库我得到一个具有我想要的结构的子目录和文件列表:

List<File> files = (List<File>) FileUtils.listFilesAndDirs(
                Environment.getExternalStorageDirectory(),
                DirectoryFileFilter.INSTANCE,
                DirectoryFileFilter.INSTANCE);

但是当我尝试使用Gson库将其序列化为JSON文件时,返回的String只包含根路径:

Gson gson = new Gson();
String json = gson.toJson(files.get(0));

输出

{
  "path": "/storage/emulated/0"
}

如何获取包含所有子目录和文件的JSON对象?

2 个答案:

答案 0 :(得分:0)

你打印什么,得到什么:

Gson gson = new Gson();
String json = gson.toJson(files.get(0)); // one file
{
  "path": "/storage/emulated/0"
}


String json = gson.toJson(files); // all files
[
  {
    "path": "/storage/emulated/0"
  },
  {
    "path": "/storage/emulated/0/dir1"
  },
  {
    "path": "/storage/emulated/dir2"
  }
]

答案 1 :(得分:0)

您的问题存在多个问题。 例如,我发现的是:

  • 假设
    • 正如Hemant Patel在答案中指出的那样,files.get(0)只返回第一个孩子。如果没有孩子,肯定会失败。
    • 另外,我从未使用过Apache Commons IO,但事实证明DirectoryFileFilter只接受目录,而你需要TrueFileFilter,正如Java文档中指出的那样。
  • 内存和一般性能问题
    • 我不确定Apache Commons IO如何在幕后工作,但返回内存中的集合LinkedList可能是一个内存问题。例如,Google Guava fileTreeTraverser返回遍历器,该遍历器允许迭代不需要将整个列表存储在内存中的迭代(但是它需要Iterable的自定义序列化器/解串器对并检测目录输入/无论如何离开)。
    • 使用String Gson.toJson(...)将可能较大的集合序列化为字符串是另一个内存问题。如果可能,您应该考虑流式传输。
  • 兼容性问题
    • 我不建议直接序列化File,因为Gson没有为它提供序列化/解串器对。从最新的Gson版本2.8.4开始,它使用ReflectiveTypeAdapterFactory$Adapter,因此它使用File字段。如果File内部结构因任何原因发生更改,您将无法对其进行反序列化。此外,拥有单个path属性不允许您区分目录和文件。

说完之后,你可能想要遍历一个文件目录,自己将每个目录和文件转换成你想要消耗的任何表示形式尽可能少的文件。

以下界面允许您构建任何JSON结构。例如:

  • 一个超级简单的单位列表:
[
    "./file1",
    "./file2",
    "./dir1",
    "./dir1/file1",
    "./dir1/file2",
    "./dir2",
    "./dir2/file1"
]
  • 打字的平面列表:
[
    {"type": "file", "path": "./file1"},
    {"type": "file", "path": "./file2"},
    {"type": "directory", "path": "./dir1"},
    {"type": "file", "path": "./dir1/file1"},
    {"type": "file", "path": "./dir1/file2"},
    {"type": "directory", "path": "./dir2"},
    {"type": "file", "path": "./dir2/file1"}
]
  • 目录/文件JSON对象,其中对象表示目录,而null表示文件:
{
    "file1": null
    "file2": null,
    "dir1": {
        "file1": null,
        "file2": null
    },
    "dir2": {
        "file1": null
    }
}
  • 目录/文件JSON数组,其中字符串表示文件,而对象表示目录:
[
    "file1"
    "file2",
    {
        "name": "dir1",
        "children": [
            "file1",
            "file2"
        ]
    },
    {
        "name": "dir2",
        "children": [
            "file1"
        ]
    }
]
interface IDirectoryWalkListener {

    void onEnterDirectory(int level, @Nonnull File directory)
            throws IOException;

    void onFile(@Nonnull File file)
            throws IOException;

    void onLeaveDirectory(int level, @Nonnull File directory)
            throws IOException;

}
final class DirectoryWalk {

    private DirectoryWalk() {
    }

    static void walk(final File root, final IDirectoryWalkListener listener)
            throws IOException {
        walk(0, root, listener);
    }

    private static void walk(final int level, final File root, final IDirectoryWalkListener listener)
            throws IOException {
        if ( !root.isDirectory() ) {
            throw new IOException(root + " must be a directory");
        }
        @Nullable
        final File[] files = root.listFiles();
        if ( files == null ) {
            throw new IOException("Cannot list files in " + root);
        }
        listener.onEnterDirectory(level, root);
        for ( final File file : files ) {
            if ( file.isDirectory() ) {
                walk(level + 1, file, listener);
            } else {
                listener.onFile(file);
            }
        }
        listener.onLeaveDirectory(level, root);
    }

}
final class ToFlatJsonArrayDirectoryWalkListener
        implements IDirectoryWalkListener {

    private final JsonWriter jsonWriter;

    private ToFlatJsonArrayDirectoryWalkListener(final JsonWriter jsonWriter) {
        this.jsonWriter = jsonWriter;
    }

    static IDirectoryWalkListener get(final JsonWriter jsonWriter) {
        return new ToFlatJsonArrayDirectoryWalkListener(jsonWriter);
    }

    @Override
    public void onEnterDirectory(final int level, @Nonnull final File directory)
            throws IOException {
        if ( level == 0 ) {
            jsonWriter.beginArray();
        }
        jsonWriter.value(directory.getPath());
    }

    @Override
    public void onFile(@Nonnull final File file)
            throws IOException {
        jsonWriter.value(file.getPath());
    }

    @Override
    public void onLeaveDirectory(final int level, @Nonnull final File directory)
            throws IOException {
        if ( level == 0 ) {
            jsonWriter.endArray();
        }
    }

}

使用示例:

// Writing to a string is a potential performance and memory issue
final Writer out = new StringWriter();
final JsonWriter jsonWriter = new JsonWriter(out);
jsonWriter.setIndent("\t");
DirectoryWalk.walk(root, ToFlatJsonArrayDirectoryWalkListener.get(jsonWriter));
System.out.println(out);
final Writer out = new OutputStreamWriter(System.out) {
    @Override
    public void close() {
        // do not close System.out
    }
};
final JsonWriter jsonWriter = new JsonWriter(out);
jsonWriter.setIndent("\t");
DirectoryWalk.walk(root, ToFlatJsonArrayDirectoryWalkListener.get(jsonWriter));
out.flush();

root./target的示例输出:

[
    "./target",
    "./target/data",
    "./target/data/journal",
    "./target/data/journal/server.lock",
    "./target/classes",
    "./target/classes/com",
    "./target/classes/com/google",
    "./target/classes/com/google/gson",
    "./target/classes/com/google/gson/interceptors",
    "./target/classes/com/google/gson/interceptors/InterceptorFactory$InterceptorAdapter.class",
    "./target/classes/com/google/gson/interceptors/InterceptorFactory$1.class",
    "./target/classes/com/google/gson/interceptors/InterceptorFactory.class",
    "./target/classes/com/google/gson/interceptors/JsonPostDeserializer.class",
    "./target/classes/com/google/gson/interceptors/Intercept.class",
    ...
]

为了从JSON恢复文件结构,您需要JsonReader(以更有效的方式使用内存)或自定义反序列化器(如果您可以将所有内容读入内存)。< / p>