package com.sedmelluq.discord.lavaplayer.tools; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /** * Allows to easily navigate in decoded JSON data */ public class JsonBrowser { public static final JsonBrowser NULL_BROWSER = new JsonBrowser(null); private static final ObjectMapper mapper = setupMapper(); private final JsonNode node; private JsonBrowser(JsonNode node) { this.node = node; } /** * @return True if the value represents a list. */ public boolean isList() { return node instanceof ArrayNode; } /** * @return True if the value represents a map. */ public boolean isMap() { return node instanceof ObjectNode; } /** * Get an element at an index for a list value * @param index List index * @return JsonBrowser instance which wraps the value at the specified index */ public JsonBrowser index(int index) { if (isList()) { return create(node.get(index)); } else { return NULL_BROWSER; } } /** * Get an element by key from a map value * @param key Map key * @return JsonBrowser instance which wraps the value with the specified key */ public JsonBrowser get(String key) { if (isMap()) { return create(node.get(key)); } else { return NULL_BROWSER; } } /** * Put a value into the map if this instance contains a map. * @param key The map entry key * @param item The map entry value */ public void put(String key, Object item) { if (node instanceof ObjectNode) { ((ObjectNode) node).set(key, mapper.valueToTree(item)); } else { throw new IllegalStateException("Put only works on a map"); } } /** * Returns a list of all the values in this element * @return The list of values as JsonBrowser elements */ public List<JsonBrowser> values() { List<JsonBrowser> values = new ArrayList<>(); if (node != null) { node.elements().forEachRemaining(child -> values.add(new JsonBrowser(child))); } return values; } /** * Attempt to retrieve the value in the specified format * @param klass The class to retrieve the value as * @return The value as an instance of the specified class * @throws IllegalArgumentException If conversion is impossible */ public <T> T as(Class<T> klass) { try { return mapper.treeToValue(node, klass); } catch (Exception e) { throw new RuntimeException(e); } } public <T> T as(TypeReference<T> type) { try { return mapper.readValue(mapper.treeAsTokens(node), type); } catch (Exception e) { throw new RuntimeException(e); } } /** * @return The value of the element as text */ public String text() { if (node != null) { if (node.isNull()) { return null; } else if (node.isTextual()) { return node.textValue(); } else if (node.isIntegralNumber()) { return String.valueOf(node.longValue()); } else if (node.isNumber()) { return node.numberValue().toString(); } else if (node.isBoolean()) { return String.valueOf(node.booleanValue()); } else { return node.toString(); } } return null; } public boolean asBoolean(boolean defaultValue) { if (node != null) { if (node.isBoolean()) { return node.booleanValue(); } else if (node.isTextual()) { if ("true".equals(node.textValue())) { return true; } else if ("false".equals(node.textValue())) { return false; } } } return defaultValue; } public long asLong(long defaultValue) { if (node != null) { if (node.isNumber()) { return node.numberValue().longValue(); } else if (node.isTextual()) { try { return Long.parseLong(node.textValue()); } catch (NumberFormatException ignored) { // Fall through to default value. } } } return defaultValue; } public String safeText() { String text = text(); return text != null ? text : ""; } public String format() { try { return node != null ? mapper.writeValueAsString(node) : null; } catch (Exception e) { return null; } } /** * @return The value of the element as text */ public boolean isNull() { return node == null || node.isNull(); } /** * Parse from string. * @param json The JSON object as a string * @return JsonBrowser instance for navigating in the result * @throws IOException When parsing the JSON failed */ public static JsonBrowser parse(String json) throws IOException { return create(mapper.readTree(json)); } /** * Parse from string. * @param stream The JSON object as a stream * @return JsonBrowser instance for navigating in the result * @throws IOException When parsing the JSON failed */ public static JsonBrowser parse(InputStream stream) throws IOException { return create(mapper.readTree(stream)); } private static ObjectMapper setupMapper() { JsonFactory jsonFactory = new JsonFactory(); jsonFactory.enable(JsonParser.Feature.ALLOW_COMMENTS); jsonFactory.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES); return new ObjectMapper(jsonFactory); } private static JsonBrowser create(JsonNode node) { return node != null ? new JsonBrowser(node) : NULL_BROWSER; } }