Я пытаюсь реализовать итератор массива JSON с парсером Jackson SAX (пожалуйста, не спрашивайте, почему). Мое приложение должно работать с огромными файлами (до 5 MiB), и это проблема.
То, как я инициализирую JsonParser и вызываю создание итератора. Я создаю InputStream, инициализированный JSON, помещенным в папку \raw.
private JsonArrayIterator getIterator(String needle) throws IOException { InputStream inputStream = getApplicationContext().getResources().openRawResource(R.raw.products); inputStream.mark(-1); try { JsonParser jsonParser = createJsonParser(inputStream); // Some unrelated code return new JsonArrayIterator(jsonParser); } catch (IOException e) { e.printStackTrace(); } finally { inputStream.close(); } return null; }
И вот мой класс итератора.
private Object parseNextObject() throws IOException { // I'm not using ObjectMapper because of reasons HashMap nextObject = new HashMap(); int objectsCount = 1; while (objectsCount > 0) { JsonToken currentToken = currentParser.nextValue(); if(currentToken == JsonToken.START_OBJECT) { ++objectsCount; } else if(currentToken == JsonToken.END_OBJECT) { --objectsCount; } else if(currentToken == JsonToken.START_ARRAY) { String currentName = currentParser.getCurrentName(); ArrayList list = new ArrayList(100); JsonArrayIterator it = new JsonArrayIterator(currentParser); while (it.hasNext()) { list.add(it.next()); } nextObject.put(currentName, list); } else { // Here exception is throwed nextObject.put(currentParser.getCurrentName(), currentParser.getText()); } } currentParser.nextToken(); // Skip END_OBJECT return nextObject; }
Кажется, он работает... О, ЖДИТЕ.
У меня есть 3 раздела (называемые массивами) в каком-то огромном файле. Он успешно разбирает сначала (крошечный, менее 1000 байт). Но я не могу разобрать дальше.
Следующий массив имеет вложенный массив с простыми объектами (например, это):
{ "properties":[ { "id":"1", "title":"\u0426\u0432\u0435\u0442", "values":[ { "id":"1_2", "title":"\u0427\u0435\u0440\u043d\u044b\u0439" }, { "id":"1_5005", "title":"\u0417\u0435\u043b\u0435\u043d\u044b\u0439" }, { "id":"1_5006", "title":"\u0421\u0435\u0440\u044b\u0439" } ] } ] }
Для одного объекта в значениях я вызываю nextObject.put. currentParser.getCurrentName() запускается успешно и возвращает правильную строку, но currentParser.getText() терпит неудачу. Это не проблема JSON: она отлично отображается на iOS. Это не проблема создания объекта или итератора: я могу удалить это, когда синтаксический анализатор генерирует исключение, но он не будет работать в одном месте.
Вот трассировка стека:
05-15 21:57:28.617: ERROR/AndroidRuntime(14758): FATAL EXCEPTION: main java.lang.NullPointerException: asset at android.content.res.AssetManager.readAsset(Native Method) at android.content.res.AssetManager.access$700(AssetManager.java:36) at android.content.res.AssetManager$AssetInputStream.read(AssetManager.java:576) at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.loadMore(UTF8StreamJsonParser.java:174) at com.fasterxml.jackson.core.base.ParserBase.loadMoreGuaranteed(ParserBase.java:425) at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._finishString2(UTF8StreamJsonParser.java:1930) at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._finishString(UTF8StreamJsonParser.java:1911) at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.getText(UTF8StreamJsonParser.java:276) at ru.studiomobile.JsonArrayIterator.parseNextObject(JsonArrayIterator.java:57) at ru.studiomobile.JsonArrayIterator.next(JsonArrayIterator.java:73) at ru.studiomobile.JsonArrayIterator.parseNextObject(JsonArrayIterator.java:47) at ru.studiomobile.JsonArrayIterator.next(JsonArrayIterator.java:73) at ru.studiomobile.MainActivity$2.onClick(MainActivity.java:81) at android.view.View.performClick(View.java:3127) at android.view.View$PerformClick.run(View.java:12025) at android.os.Handler.handleCallback(Handler.java:587) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:132) at android.app.ActivityThread.main(ActivityThread.java:4126) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:491) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602) at dalvik.system.NativeStart.main(Native Method)
Я заметил, что есть что-то, что называется Utf8StreamJsonParser. Он имеет поле с именем _inputEnd равным 4000 (почему 4000?). Когда другое поле, _inputPtr, становится больше, оно генерирует исключение. Как я могу справиться с этим? Я попытался использовать BufferedInputStream с предопределенным размером блока вместо InputStream, но это не повлияло.
Информация о некоторых строках
47: list.add(it.next()); 73: return parseNextObject(); 75: e.printStackTrace();
Ничего особенного.
Это была очевидная проблема: я забыл, что закрываю поток после создания парсера...