Created
May 30, 2023 11:21
-
-
Save Daomephsta/400918da69d0e4dae23e990cf12ab925 to your computer and use it in GitHub Desktop.
JsonStack
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.github.daomephsta; | |
import java.util.ArrayDeque; | |
import java.util.Deque; | |
import java.util.Iterator; | |
import com.google.gson.JsonArray; | |
import com.google.gson.JsonElement; | |
import com.google.gson.JsonObject; | |
import com.google.gson.JsonPrimitive; | |
import com.google.gson.JsonSyntaxException; | |
public class JsonStack | |
{ | |
private final Deque<Element> elements = new ArrayDeque<>(); | |
private JsonStack(Element root) | |
{ | |
elements.add(root); | |
} | |
public static JsonStack ofObject(JsonElement root, String rootName) | |
{ | |
if (!root.isJsonObject()) | |
throw new IllegalArgumentException("Root must be JsonObject not " + root.getClass().getSimpleName()); | |
return new JsonStack(new ObjectMember(root, rootName)); | |
} | |
public JsonStack pushKey(String key) | |
{ | |
var head = getHead(JsonObject.class); | |
if (!head.has(key)) | |
throw new JsonSyntaxException("%s/%s is missing".formatted(joinPath(), key)); | |
elements.push(new ObjectMember(head.get(key), key)); | |
return this; | |
} | |
public JsonStack pushIndex(int index) | |
{ | |
var head = getHead(JsonArray.class); | |
if (head.size() <= index) | |
throw new JsonSyntaxException("%s[%d] is missing".formatted(joinPath(), index)); | |
elements.push(new ArrayElement(head.get(index), index)); | |
return this; | |
} | |
public JsonStack push(Object... keysAndIndices) | |
{ | |
for (Object object : keysAndIndices) | |
{ | |
if (object instanceof String key) | |
pushKey(key); | |
else if (object instanceof Integer index) | |
pushIndex(index); | |
else | |
throw new IllegalStateException(object + " is not a key or index"); | |
} | |
return this; | |
} | |
public JsonStack pop() | |
{ | |
if ((elements.size()) == 1) | |
throw new IllegalStateException("Cannot pop root element"); | |
elements.pop(); | |
return this; | |
} | |
public JsonStack pop(int levels) | |
{ | |
for (int i = 0; i < levels; i++) | |
pop(); | |
return this; | |
} | |
public boolean getBoolean() | |
{ | |
var head = getHead(JsonPrimitive.class); | |
if (!head.isBoolean()) | |
throw new IllegalArgumentException(joinPath() + " must be JsonBoolean"); | |
return head.getAsBoolean(); | |
} | |
public Number getNumber() | |
{ | |
var head = getHead(JsonPrimitive.class); | |
if (!head.isNumber()) | |
throw new IllegalArgumentException(joinPath() + " must be JsonNumber"); | |
return head.getAsNumber(); | |
} | |
public byte getByte() | |
{ | |
return getNumber().byteValue(); | |
} | |
public short getShort() | |
{ | |
return getNumber().shortValue(); | |
} | |
public int getInt() | |
{ | |
return getNumber().intValue(); | |
} | |
public long getLong() | |
{ | |
return getNumber().longValue(); | |
} | |
public float getFloat() | |
{ | |
return getNumber().floatValue(); | |
} | |
public double getDouble() | |
{ | |
return getNumber().doubleValue(); | |
} | |
public String getString() | |
{ | |
var head = getHead(JsonPrimitive.class); | |
if (!head.isString()) | |
throw new IllegalArgumentException(joinPath() + " must be JsonString"); | |
return head.getAsString(); | |
} | |
public JsonObject getObject() | |
{ | |
return getHead(JsonObject.class); | |
} | |
public JsonArray getArray() | |
{ | |
return getHead(JsonArray.class); | |
} | |
public JsonElement get() | |
{ | |
return elements.peek().json(); | |
} | |
private <T extends JsonElement> T getHead(Class<T> expectedType) | |
{ | |
JsonElement head = get(); | |
if (expectedType.isInstance(head)) | |
return expectedType.cast(head); | |
throw new JsonSyntaxException("%s must be %s not %s".formatted( | |
joinPath(), expectedType.getSimpleName(), head.getClass().getSimpleName())); | |
} | |
private sealed interface Element | |
{ | |
public JsonElement json(); | |
} | |
private record ArrayElement(JsonElement json, int index) implements Element {} | |
private record ObjectMember(JsonElement json, String key) implements Element {} | |
private String joinPath() | |
{ | |
var path = new StringBuilder(); | |
for (Iterator<Element> iter = elements.descendingIterator(); iter.hasNext();) | |
{ | |
Element element = iter.next(); | |
if (element instanceof ObjectMember member) | |
path.append(path.isEmpty() ? member.key : "/%s".formatted(member.key)); | |
else if (element instanceof ArrayElement arrayElement) | |
path.append("[%d]".formatted(arrayElement.index)); | |
} | |
return path.toString(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment