大きなJSON を Google gson で読込むとき、メモリ不足にならないように、fromJson ではなく
JSONのトークンで読みだす JsonReader を実行するのだが、注意しなければならない点がある。
以下のように、JsonReader の hasNext() 問い合わせだけでは、END_ARRAY、END_OBJECT を
見つけることができず想定どおりの読出しができない。
try(JsonReader reader = new JsonReader(new InputStreamReader( new FileInputStream(jsonFilePath), StandardCharsets.UTF_8))){ while((reader.hasNext()){| // }
END_ARRAY、END_OBJECT、END_DOCUMENT をチェックするようにする必要がある。
try(JsonReader reader = new JsonReader(new InputStreamReader( new FileInputStream(jsonFilePath), StandardCharsets.UTF_8))){ while((reader.hasNext() || reader.peek().equals(JsonToken.END_ARRAY) || reader.peek().equals(JsonToken.END_OBJECT) ) && !reader.peek().equals(JsonToken.END_DOCUMENT) ){ JsonToken token = reader.peek(); StringBuilder sb = new StringBuilder(); sb.append(token); String str; if (token.equals(JsonToken.BEGIN_ARRAY)){ reader.beginArray(); }else if(token.equals(JsonToken.END_ARRAY)){ reader.endArray(); }else if(token.equals(JsonToken.BEGIN_OBJECT)){ reader.beginObject(); }else if(token.equals(JsonToken.END_OBJECT)){ reader.endObject(); }else if(token.equals(JsonToken.NAME)){ str = reader.nextName(); }else if(token.equals(JsonToken.STRING)){ str = reader.nextString(); sb.append(":"+reader.getPath()+" = "); sb.append(str); }else if(token.equals(JsonToken.NUMBER)){ long l = reader.nextLong(); sb.append(":"+reader.getPath()+" = "); sb.append(l); }else if(token.equals(JsonToken.BOOLEAN)){ Boolean b = reader.nextBoolean(); sb.append(":"+reader.getPath()+" = "); sb.append(b); }else if(token.equals(JsonToken.NULL)){ reader.nextNull(); sb.append(":"+reader.getPath()+" = "); sb.append("NULL"); } System.out.println(sb.toString()); } }catch(IOException e){ e.printStackTrace(); }
このコードを見ると長くてそんなに綺麗なコードではない、けれど、
Google gson の JsonReader が優れている点は、階層が深くなっても
再帰メソッドを書く必要がなくて、
この while ループで事足りることだ。