Skip to content

Instantly share code, notes, and snippets.

@Daomephsta
Created May 30, 2023 11:21
Show Gist options
  • Save Daomephsta/400918da69d0e4dae23e990cf12ab925 to your computer and use it in GitHub Desktop.
Save Daomephsta/400918da69d0e4dae23e990cf12ab925 to your computer and use it in GitHub Desktop.
JsonStack
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