/******************************************************************************* * Copyright (c) 2016 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package melnorme.lang.utils.gson; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import com.google.gson.internal.bind.TypeAdapters; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.MalformedJsonException; import melnorme.utilbox.core.CommonException; /** * An alternative to {@link JsonParser}, which preservers the statically-checked exception throwing API */ public class JsonParserX { public static JsonReader newReader(String json, boolean lenient) { return newReader(new StringReader(json), lenient); } public static JsonReader newReader(Reader reader, boolean lenient) { JsonReader jsonReader = new JsonReader(reader); jsonReader.setLenient(lenient); return jsonReader; } /* ----------------- ----------------- */ public JsonElement parse(String json, boolean lenient) throws IOException, JsonSyntaxExceptionX { return parse(new StringReader(json), lenient); } public JsonElement parse(Reader reader, boolean lenient) throws IOException, JsonSyntaxExceptionX { JsonReader json = new JsonReader(reader); boolean originalLenient = json.isLenient(); json.setLenient(lenient); try { return parse(json); } finally { json.setLenient(originalLenient); } } public JsonElement parse(JsonReader reader) throws IOException, JsonSyntaxExceptionX { // Based on GSON 2.2.4 code of Streams.parse(json); try { return TypeAdapters.JSON_ELEMENT.read(reader); } catch(MalformedJsonException e) { throw new JsonSyntaxExceptionX(e); } catch(NumberFormatException e) { throw new JsonSyntaxExceptionX(e); } catch(JsonParseException e) { // I don't think JsonParseException is ever thrown from code above, // not with normal JsonReader at least (a subclass might though). // So just in case, sanitize it: throw new JsonSyntaxExceptionX(e.getCause()); } } @SuppressWarnings("serial") public static class JsonSyntaxExceptionX extends Exception { public JsonSyntaxExceptionX(Throwable cause) { super(cause); } } /* ----------------- ----------------- */ public JsonElement parse_common(JsonReader reader) throws CommonException { try { return parse(reader); } catch(IOException e) { throw new CommonException("Error reading from input: ", e); } catch(JsonSyntaxExceptionX e) { throw new CommonException("JSON syntax error: ", e); } } /* ----------------- ----------------- */ public JsonObject parseObject(JsonReader reader) throws CommonException { JsonElement element = parse_common(reader); if(element.isJsonObject()) { return element.getAsJsonObject(); } else { throw new CommonException("Parsed JSON element is not a JSON object."); } } public JsonObject parseObject(Reader json, boolean lenient) throws CommonException { return parseObject(newReader(json, lenient)); } public JsonObject parseObject(String json, boolean lenient) throws CommonException { return parseObject(newReader(json, lenient)); } public static boolean isEndOfInput(JsonReader jsonReader) throws IOException { try { return jsonReader.peek() == JsonToken.END_DOCUMENT; } catch(IOException e) { // Why the hell GSON doesn't have a proper way to check for empty document? // Need to review on future versions if(e.getMessage().startsWith("End of input")) { return true; } throw e; } } }