package com.xmage.ws.util.json; import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; import net.minidev.json.JSONValue; import java.util.HashMap; import java.util.Map; /** * Enhances working with json. * * @author noxx */ public class JSONParser { public enum CachePolicy { CACHE_ONE_LEVEL_ONLY, CACHE_ALL_LEVELS } private static final Map<String, Integer> extendedIndexes = new HashMap<String, Integer>() {{ put("$first", 0); put("$second", 1); put("$third", 2); put("$fourth", 3); put("$fifth", 4); }}; private String json; private JSONObject root; private boolean hitCache; private CachePolicy cachePolicy = CachePolicy.CACHE_ONE_LEVEL_ONLY; private Map<String, Object> cache = new HashMap<>(); public void parseJSON(String jsonString) throws JSONValidationException { parseJSON(jsonString, true); } public void parseJSON(String jsonString, boolean validate) throws JSONValidationException { this.json = jsonString; prepare(); if (validate) { validate(); } } public Object get(String path) { return getObject(path); } public int getInt(String path) { return (Integer)getObject(path); } public int getIntSafe(String path) { if (getObject(path) == null) { return 0; } return (Integer)getObject(path); } public String getString(String path) { return (String)getObject(path); } public JSONObject getJSON(String path) { return (JSONObject)getObject(path); } private Object getObject(String path) { this.hitCache = false; if (cache.containsKey(path)) { this.hitCache = true; return cache.get(path); } String[] params = path.split("\\."); JSONObject json = this.root; JSONArray jsonArray = null; String currentPath = ""; for (int i = 0; i < params.length - 1; i++) { String param = params[i]; if (cachePolicy == CachePolicy.CACHE_ALL_LEVELS) { if (!currentPath.isEmpty()) { currentPath += "."; } currentPath += param; } if (param.startsWith("$")) { if (jsonArray == null) { throw new JSONOperationErrorException("Not illegal syntax at this place: " + param); } int index = getIndex(param); json = (JSONObject) jsonArray.get(index); jsonArray = null; } else if (param.contains("[")) { int find = param.indexOf('['); String newParam = param.substring(0, find); String s = param.substring(find+1, param.indexOf(']')); if (s.isEmpty()) { jsonArray = (JSONArray) json.get(newParam); json = null; } else { int index = Integer.parseInt(s); json = (JSONObject)((JSONArray) json.get(newParam)).get(index); jsonArray = null; } } else { Object obj = json.get(param); if (obj instanceof JSONObject) { json = (JSONObject) obj; jsonArray = null; } else if (obj instanceof JSONArray) { jsonArray = (JSONArray) obj; json = null; } else if (obj == null) { throw new IllegalStateException("json object is null"); } else { throw new IllegalStateException("json object ('"+param+"') has wrong type: " + obj.getClass()); } } if (cachePolicy == CachePolicy.CACHE_ALL_LEVELS) { saveToCache(currentPath, json); } } String name = params[params.length - 1]; Object value; if (name.startsWith("$")) { if (jsonArray == null) { throw new JSONOperationErrorException("Not illegal syntax at this place: " + name); } int index = getIndex(name); value = jsonArray.get(index); } else { value = json.get(name); } saveToCache(path, value); return value; } private int getIndex(String extendedIndex) { if (extendedIndexes.containsKey(extendedIndex)) { return extendedIndexes.get(extendedIndex); } else { throw new JSONOperationErrorException("Can't parse extended index: " + extendedIndex); } } private void saveToCache(String path, Object value) { cache.put(path, value); } public JSONArray getJSONArray(String path) { return (JSONArray)getObject(path); } private void prepare() { reset(); if (this.json != null) { this.json = this.json.trim(); } } private void validate() throws JSONValidationException { if (this.json == null) { throw new JSONValidationException("JSON is null"); } try { this.root = (JSONObject) JSONValue.parse(this.json); if (this.root == null) { throw new JSONValidationException("Root json is null"); } } catch (Exception e) { throw new JSONValidationException("JSON is not valid", e); } } public void reset() { this.hitCache = false; this.cachePolicy = CachePolicy.CACHE_ONE_LEVEL_ONLY; this.cache.clear(); } public boolean isHitCache() { return hitCache; } public void setCachePolicy(CachePolicy cachePolicy) { this.cachePolicy = cachePolicy; } }