I try to connect an app to an external service which provides me either an array like
[{"descr":"my description","id":"123"},...]
or in case of an error something like
{"error":{"code":123,"message":"my message"}}
Is there a way to define this as a single class or to recognize the stream by which object is it?
Update 1: I’ve tested several versions by runnig them 100 times in an loop and calculation average execution time. In all the different versions I’ve used jackson.
This one is the baseline for the performance. It doesn’t fulfill the business requirements because the code doesn’t distinguishes between arrays and obejcts.
fcPhotos = mJsonToObjectMapper.readValue(mFcHttpHelper.doGet(urlString), FcPhotos.class);
Photos: 100 executions with an avg. duration of: 923ms
Using a parser decreases the performance by about min. 50%
JsonParser jsonParser = mJsonFactory.createJsonParser(mFcHttpHelper.doGet(urlString));
if (jsonParser.nextToken() == JsonToken.START_ARRAY) {
fcPhotos = mJsonToObjectMapper.readValue(jsonParser, FcPhotos.class);
}
Photos: 100 executions with an avg. duration of: 1585ms
Converting the InputStream into an String and keeping the objectMapper.readValue solution leads to:
jsonString = inputStreamToString(urlString);
fcPhotos = mJsonToObjectMapper.readValue(jsonString, FcPhotos.class);
private final String inputStreamToString(final String urlString) throws UnsupportedEncodingException, IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(mFcHttpHelper.doGet(urlString), "UTF8"), 8192);
StringBuilder stringBuilder = new StringBuilder();
String line = null;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + "\n");
}
bufferedReader.close();
return stringBuilder.toString();
}
Photos: 100 executions with an avg. duration of: 1191ms
end of update 1
One possibility would be to use JsonParser to see what is the first token (JsonToken.START_ARRAY or JsonToken.START_OBJECT), and then do data binding. ObjectMapper can take JsonParser that either points to the first event, or has not yet been moved to the first event. Combining of JsonParser/ObjectMapper is also good when processing a stream of root-level JSON values (like would be the case for map/reduce style streams, or log entries when using JSON).
Another way would be to first bind to tree model (JsonNode; ObjectMapper.readTree()); see if node is an ObjectNode or ArrayNode, and then use ObjectMapper.convertValue(rootNode, desiredClass). This has the benefit that you could also check out properties of the tree, in case there is no array vs object distinction.