package cz.cuni.mff.d3s.been.util; import static org.codehaus.jackson.JsonToken.*; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cz.cuni.mff.d3s.been.annotation.NotThreadSafe; /** * The JsonStreamer is rather limited streaming JSON streaming parser. * * It visits every "key": value pair and calls a handler if such a handler is * registered for the key . The key point is that it does not handle sub-object * or array, it simple ignores them. * * @author Martin Sixta */ @NotThreadSafe public class JsonStreamer { /** logging */ private static final Logger log = LoggerFactory.getLogger(JsonStreamer.class); /** Factory */ private final JsonFactory jsonFactory; /** Registered handlers */ private final Map<String, JsonKeyHandler> handlers; /** * Create a JSON streamer */ public JsonStreamer() { this.jsonFactory = new JsonFactory(); this.handlers = new HashMap<>(); } /** * Adds a handler for a key. * * If a handler for the given key exits it will be overridden. * * @param key * key on which to call the handler * @param handler * code to call */ public void addHandler(String key, JsonKeyHandler handler) { handlers.put(key, handler); } /** * Removes a handler fot a key. * * @param key * associated key */ public void removeHandler(String key) { handlers.remove(key); } /** * * Parses a JSON String and calls registered handlers on matched JSON fragments. * * @param json JSON to process * * @throws JsonException On failure to create a JSON parser */ public void process(final String json) throws JsonException { if (handlers.size() == 0) { return; } JsonParser jp; try { jp = jsonFactory.createJsonParser(json); } catch (Throwable t) { String msg = String.format("Cannot parse JSON: %s", json); throw new JsonException(msg, t); } JsonToken currentToken; try { jp.nextToken(); while (true) { currentToken = jp.nextToken(); if (currentToken == null) { break; } if (currentToken != FIELD_NAME) { continue; } final String key = jp.getCurrentName(); currentToken = jp.nextToken(); if (currentToken == START_ARRAY || currentToken == START_OBJECT) { continue; } if (handlers.containsKey(key)) { callHandler(key, jp.getText(), json); } } } catch (Throwable t) { String msg = String.format("Cannot process JSON token '%s'", jp.getCurrentToken()); throw new JsonException(msg, t); } } private void callHandler(final String key, final String value, final String json) throws IOException { try { handlers.get(key).handle(key, value, json); } catch (Exception e) { log.error("Handler caused an exception", e); } } }