Получить дерево файлов с библиотекой Gson - PullRequest
0 голосов
/ 06 мая 2018

Я пытаюсь получить файл JSON со структурой каталога, включая файлы и подкаталоги рекурсивно.

Используя библиотеку apache-commons-io, я получаю список подкаталогов и файлов с нужной мне структурой:

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

Но когда я пытаюсь сериализовать его в файл JSON с использованием библиотеки Gson, возвращаемая строка содержит только корневой путь:

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

Выход:

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

Как получить объект JSON со всеми вложенными подкаталогами и файлами?

Ответы [ 2 ]

0 голосов
/ 07 мая 2018

У вас есть несколько вопросов с вашим вопросом. Например, что я нашел:

  • Предположение
    • Как было указано в ответе Хемант Патель , files.get(0) просто возвращает первого ребенка. Если точно не получится, если нет детей.
    • Кроме того, я никогда не использовал Apache Commons IO, но оказывается, что DirectoryFileFilter принимает только каталоги, и вам нужно TrueFileFilter, как указано в документации Java.
  • Память и общие проблемы с производительностью
    • Я не уверен, как Apache Commons IO работает за кулисами, но возвращение коллекции в памяти, LinkedList, вероятно, проблема с памятью. Например, Google Guava fileTreeTraverser возвращает traverser, который позволяет перебирать итерируемое, не требуя сохранения всего списка в памяти (однако для 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 (для более эффективного использования памяти) или пользовательский десериализатор (если вы хорошо читаете все данные в память).

0 голосов
/ 06 мая 2018

Что вы печатаете, что вы получаете:

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"
  }
]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...