Skip to content

Instantly share code, notes, and snippets.

@forax
Created July 31, 2014 00:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save forax/81a56cf2684bfa2e46ec to your computer and use it in GitHub Desktop.
Save forax/81a56cf2684bfa2e46ec to your computer and use it in GitHub Desktop.
Json parser 'modern' API in one file
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.json.Json;
import javax.json.stream.JsonParser;
import javax.json.stream.JsonParser.Event;
public class JsonAPI {
public static final Object SKIP = new Object();
public interface ObjectVisitor<T, R> {
T visitStartObject();
ObjectVisitor<?, ?> visitFieldObject(String key);
ArrayVisitor<?, ?> visitFieldArray(String key);
default Object visitFieldPrimitiveValue(String key, Object value) { return value; }
void visitEachFieldValue(T object, String key, Object value);
R visitEndObject(T result);
}
public interface ArrayVisitor<V, R> {
Function<? super Stream<V>, ? extends R> visitStartArray();
ObjectVisitor<?, ?> visitObject();
ArrayVisitor<?, ?> visitArray();
default Object visitPrimitiveValue(Object value) { return value; }
V visitEachValue(Object value);
default R visitEndArray(R result) { return result; }
}
// --- parser implementation
static final Object POISON = new Object();
public static <T, R> R parseObject(Reader reader, ObjectVisitor<T, R> objectVisitor) {
Objects.requireNonNull(objectVisitor);
try (JsonParser parser = Json.createParser(reader)) {
return parseObject(parser, (T)null, objectVisitor);
}
}
public static <V, R> R parseArray(Reader reader, ArrayVisitor<V, R> arrayVisitor) {
Objects.requireNonNull(arrayVisitor);
try (JsonParser parser = Json.createParser(reader)) {
Event event = parser.next();
if (event != Event.START_ARRAY) {
throw new AssertionError();
}
return arrayVisitor.visitEndArray(arrayVisitor.visitStartArray().apply(createStream(parser, arrayVisitor)));
}
}
private static <T, R> R parseObject(JsonParser parser, T object, ObjectVisitor<T, R> ov) {
String key = null;
for (;;) {
JsonParser.Event event = parser.next();
switch(event) {
case START_OBJECT:
if (key != null) {
ObjectVisitor<Object,?> newOv = (ObjectVisitor<Object,?>)ov.visitFieldObject(key);
if (newOv != null) {
Object newObject = newOv.visitStartObject();
Object value = parseObject(parser, newObject, newOv);
ov.visitEachFieldValue(object, key, value);
} else {
skipUntil(parser, Event.START_OBJECT, Event.END_OBJECT);
}
key = null;
} else {
assert object == null;
object = ov.visitStartObject();
}
break;
case END_OBJECT:
return ov.visitEndObject(object);
case KEY_NAME:
key = parser.getString();
break;
case START_ARRAY: {
assert key != null;
ArrayVisitor<?, ?> av = ov.visitFieldArray(key);
if (av != null) {
Stream<?> stream = createStream(parser, av);
Function<Stream<?>, ?> fun = (Function<Stream<?>, Object>)(Function<?,?>)av.visitStartArray();
Object value = fun.apply(stream);
ov.visitEachFieldValue(object, key, value);
} else {
skipUntil(parser, Event.START_ARRAY, Event.END_ARRAY);
}
key = null;
break;
}
//case END_ARRAY:
default:
throw new AssertionError();
case VALUE_NULL:
visitFieldPrimitive(ov, object, key, null);
key = null;
break;
case VALUE_FALSE: {
visitFieldPrimitive(ov, object, key, false);
key = null;
break;
}
case VALUE_TRUE: {
visitFieldPrimitive(ov, object, key, true);
key = null;
break;
}
case VALUE_STRING: {
visitFieldPrimitive(ov, object, key, parser.getString());
key = null;
break;
}
case VALUE_NUMBER: {
Object value = parser.isIntegralNumber()? parser.getLong(): parser.getBigDecimal();
visitFieldPrimitive(ov, object, key, value);
key = null;
break;
}
}
}
}
private static <T> void visitFieldPrimitive(ObjectVisitor<T, ?> ov, T object, String key, Object value) {
value = ov.visitFieldPrimitiveValue(key, value);
if (value != SKIP) {
ov.visitEachFieldValue(object, key, value);
}
}
static Object parseArrayValue(JsonParser parser, ArrayVisitor<?, ?> av) {
JsonParser.Event event = parser.next();
switch(event) {
case START_OBJECT: {
ObjectVisitor<Object,?> ov = (ObjectVisitor<Object,?>)av.visitObject();
if (ov != null) {
Object object = ov.visitStartObject();
Object value = parseObject(parser, object, ov);
return av.visitEachValue(value);
}
skipUntil(parser, Event.START_OBJECT, Event.END_OBJECT);
return SKIP;
}
//case END_OBJECT:
//case KEY_NAME:
default:
throw new AssertionError();
case START_ARRAY: {
ArrayVisitor<?, ?> newAv = av.visitArray();
if (newAv == null) {
Stream<?> stream = createStream(parser, newAv);
Function<Stream<?>, ?> fun = (Function<Stream<?>, Object>)(Function<?, ?>)newAv.visitStartArray();
Object value = fun.apply(stream);
return av.visitEachValue(value);
}
skipUntil(parser, Event.START_ARRAY, Event.END_ARRAY);
return SKIP;
}
case END_ARRAY:
return POISON;
case VALUE_NULL:
return visitPrimitive(av, null);
case VALUE_FALSE:
return visitPrimitive(av, false);
case VALUE_TRUE:
return visitPrimitive(av, true);
case VALUE_STRING:
return visitPrimitive(av, parser.getString());
case VALUE_NUMBER: {
Object value = parser.isIntegralNumber()? parser.getLong(): parser.getBigDecimal();
return visitPrimitive(av, value);
}
}
}
private static Object visitPrimitive(ArrayVisitor<?, ?> av, Object value) {
value = av.visitPrimitiveValue(value);
return value != SKIP? av.visitEachValue(value): SKIP;
}
private static void skipUntil(JsonParser parser, JsonParser.Event startEvent, JsonParser.Event endEvent) {
int depth = 0;
for(;;) {
Event event = parser.next();
if (event == endEvent) {
if (depth == 0) {
return;
}
depth--;
continue;
}
if (event == startEvent) {
depth++;
continue;
}
}
}
static <V> Stream<V> createStream(JsonParser parser, ArrayVisitor<V, ?> av) {
return StreamSupport.stream(new Spliterator<V>() {
@Override
public boolean tryAdvance(Consumer<? super V> action) {
Object value = parseArrayValue(parser, av);
if (value == POISON) {
return false;
}
if (value == SKIP) {
return true;
}
action.accept((V)value);
return true;
}
@Override
public Spliterator<V> trySplit() {
return null;
}
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public int characteristics() {
return 0;
}
}, false);
}
// --- use the visitors to create a tree of values
public static Map<String, Object> parseObjectAsMap(Reader reader) {
return parseObject(reader, COLLECTION_VISITOR);
}
public static <V> Stream<V> parseArrayAsStream(Reader reader, ArrayVisitor<V, ?> arrayVisitor) {
try (JsonParser parser = Json.createParser(reader)) {
Event event = parser.next();
if (event != Event.START_ARRAY) {
throw new IllegalStateException("bad event " + event);
}
return createStream(parser, arrayVisitor);
}
}
public static Stream<Object> parseArrayAsStream(Reader reader) {
return parseArrayAsStream(reader, arrayVisitor());
}
static class TheVisitor<O, A> implements ObjectVisitor<Map<String, Object>, O>,
ArrayVisitor<Object, A> {
private final Function<? super Map<String,Object>, ? extends O> objectMapper;
private final Function<? super Stream<Object>, ? extends A> arrayMapper;
private final ObjectVisitor<?, ?> objectVisitor;
private final ArrayVisitor<?, ?> arrayVisitor;
TheVisitor(Function<? super Map<String, Object>, ? extends O> objectMapper,
Function<? super Stream<Object>, ? extends A> arrayMapper,
ObjectVisitor<?, ?> objectVisitor,
ArrayVisitor<?, ?> arrayVisitor) {
this.objectMapper = Objects.requireNonNull(objectMapper);
this.arrayMapper = Objects.requireNonNull(arrayMapper);
this.objectVisitor = objectVisitor;
this.arrayVisitor = arrayVisitor;
}
TheVisitor(Function<? super Map<String, Object>, ? extends O> objectMapper,
Function<? super Stream<Object>, ? extends A> arrayMapper) {
this.objectMapper = Objects.requireNonNull(objectMapper);
this.arrayMapper = Objects.requireNonNull(arrayMapper);
this.objectVisitor = this;
this.arrayVisitor = this;
}
@Override
public Map<String, Object> visitStartObject() { return new HashMap<>(); }
@Override
public ObjectVisitor<?, ?> visitFieldObject(String key) { return objectVisitor; }
@Override
public ArrayVisitor<?, ?> visitFieldArray(String key) { return arrayVisitor; }
@Override
public void visitEachFieldValue(Map<String, Object> map, String key, Object value) {
map.put(key, value);
}
@Override
public O visitEndObject(Map<String, Object> result) { return objectMapper.apply(result); }
@Override
public Function<? super Stream<Object>, ? extends A> visitStartArray() {
return arrayMapper;
}
@Override
public ObjectVisitor<?, ?> visitObject() { return objectVisitor; }
@Override
public ArrayVisitor<?, ?> visitArray() { return arrayVisitor; }
@Override
public Object visitEachValue(Object value) { return value; }
}
private static final TheVisitor<Map<String, Object>, List<Object>> COLLECTION_VISITOR =
new TheVisitor<>(Function.identity(), stream -> stream.collect(Collectors.toList()));
public static ObjectVisitor<Map<String, Object>, Map<String, Object>> objectVisitor() {
return COLLECTION_VISITOR;
}
public static ArrayVisitor<Object, List<Object>> arrayVisitor() {
return COLLECTION_VISITOR;
}
public static <O> ObjectVisitor<Map<String, Object>, O> objectVisitor(
Function<? super Map<String, Object>, ? extends O> mapper,
ObjectVisitor<?, ?> objectVisitor,
ArrayVisitor<?, ?> arrayVisitor) {
return new TheVisitor<>(mapper, stream -> null, objectVisitor, arrayVisitor);
}
public static <A> ArrayVisitor<Object, A> arrayVisitor(
Function<? super Stream<Object>, ? extends A> mapper,
ObjectVisitor<?, ?> objectVisitor,
ArrayVisitor<?, ?> arrayVisitor) {
return new TheVisitor<>(map -> null, mapper, objectVisitor, arrayVisitor);
}
// --- JSON Map and Array object
public static class JsonObject {
private final Map<String, Object> map;
public JsonObject(Map<String, Object> map) {
this.map = Objects.requireNonNull(map);
}
public Object getObject(String key) { return map.get(key); }
public String getString(String key) { return (String)map.get(key); }
public int getInt(String key) { return (int)(long)map.get(key); }
public long getLong(String key) { return (long)map.get(key); }
public BigDecimal getBigDecimal(String key) { return (BigDecimal)map.get(key); }
public JsonObject getJsonObject(String key) { return (JsonObject)map.get(key); }
public JsonArray getJsonArray(String key) { return (JsonArray)map.get(key); }
public Map<String, Object> asMap() { return Collections.unmodifiableMap(map); }
}
public static class JsonArray {
private final List<Object> list;
public JsonArray(List<Object> list) {
this.list = Objects.requireNonNull(list);
}
public Object getObject(int index) { return list.get(index); }
public String getString(int index) { return (String)list.get(index); }
public int getInt(int index) { return (int)(long)list.get(index); }
public long getLong(int index) { return (long)list.get(index); }
public BigDecimal getBigDecimal(int index) { return (BigDecimal)list.get(index); }
public JsonObject getJsonObject(int index) { return (JsonObject)list.get(index); }
public JsonArray getJsonArray(int index) { return (JsonArray)list.get(index); }
public List<Object> asList() { return Collections.unmodifiableList(list); }
}
private static final TheVisitor<JsonObject, JsonArray> JSON_VISITOR =
new TheVisitor<>(JsonObject::new, stream -> new JsonArray(stream.collect(Collectors.toList())));
public static ObjectVisitor<Map<String, Object>, JsonObject> jsonObjectVisitor() {
return JSON_VISITOR;
}
public static ArrayVisitor<Object, JsonArray> jsonArrayVisitor() {
return JSON_VISITOR;
}
public static <O> ObjectVisitor<Map<String, Object>, O> jsonObjectVisitor(
Function<? super JsonObject, ? extends O> mapper,
ObjectVisitor<?, ?> objectVisitor,
ArrayVisitor<?, ?> arrayVisitor) {
Objects.requireNonNull(mapper);
return objectVisitor(map -> mapper.apply(new JsonObject(map)), objectVisitor, arrayVisitor);
}
public static <A> ArrayVisitor<Object, A> jsonArrayVisitor(
Function<? super JsonArray, ? extends A> mapper,
ObjectVisitor<?, ?> objectVisitor,
ArrayVisitor<?, ?> arrayVisitor) {
Objects.requireNonNull(mapper);
return arrayVisitor(stream -> mapper.apply(new JsonArray(stream.collect(Collectors.toList()))), objectVisitor, arrayVisitor);
}
// --- printer
static class toStringVisitor implements ObjectVisitor<StringBuilder, String>, ArrayVisitor<String, String> {
@Override
public StringBuilder visitStartObject() { return new StringBuilder().append('{'); }
@Override
public ObjectVisitor<?, ?> visitFieldObject(String key) { return this; }
@Override
public ArrayVisitor<?, ?> visitFieldArray(String key) { return this; }
@Override
public Object visitFieldPrimitiveValue(String key, Object value) {
return visitPrimitiveValue(value);
}
@Override
public void visitEachFieldValue(StringBuilder builder, String key, Object value) {
if (builder.length() != 1) {
builder.append(", ");
}
builder.append("\"").append(key).append("\": ").append((String)value);
}
@Override
public String visitEndObject(StringBuilder result) { return result.append('}').toString(); }
@Override
public Function<? super Stream<String>, ? extends String> visitStartArray() {
return stream -> stream.collect(Collectors.joining(", ", "[", "]"));
}
@Override
public ObjectVisitor<?, ?> visitObject() {
return this;
}
@Override
public ArrayVisitor<?, ?> visitArray() {
return this;
}
@Override
public Object visitPrimitiveValue(Object value) {
if (value instanceof String) {
return '"' + (String)value + '"';
}
return String.valueOf(value);
}
@Override
public String visitEachValue(Object value) {
return (String)value;
}
}
public static ObjectVisitor<StringBuilder, String> toStringObjectVisitor() {
return TO_STRING_VISITOR;
}
public static ArrayVisitor<String, String> toStringArrayVisitor() {
return TO_STRING_VISITOR;
}
private static final toStringVisitor TO_STRING_VISITOR = new toStringVisitor();
// --- examples
public static void main(String[] args) {
StringReader reader1 = new StringReader("{ \"foo\": 3, \"bar\": 4 }");
Map<String, Object> map1 = parseObjectAsMap(reader1);
System.out.println(map1);
StringReader reader2 = new StringReader("[ \"foo\", 3, \"bar\", 4 ]");
Stream<String> stream2 = parseArrayAsStream(reader2).map(String::valueOf);
stream2.forEach(System.out::println);
StringReader reader3 = new StringReader("[ \"foo\", \"bar\" ]");
Stream<String> stream3 = parseArrayAsStream(reader3).map(String.class::cast);
stream3.forEach(System.out::println);
StringReader reader4 = new StringReader("{ \"foo\": 3, \"bar\": [ { \"foo\": 5, \"baz\": 4}] }");
Map<String, Object> map4 = parseObject(reader4, objectVisitor());
System.out.println(map4);
StringReader reader5 = new StringReader("[ \"foo\", \"bar\" ]");
List<Object> map5 = parseArray(reader5, arrayVisitor());
System.out.println(map5);
StringReader reader6 = new StringReader("{ \"year\": 2014, \"month\": 1, \"dayOfMonth\": 1 }");
JsonObject object6 = parseObject(reader6, jsonObjectVisitor());
System.out.println(LocalDate.of(object6.getInt("year"), object6.getInt("month"), object6.getInt("dayOfMonth")));
StringReader reader7 = new StringReader("[ 666, 222 ]");
JsonArray array7 = parseArray(reader7, jsonArrayVisitor());
System.out.println(array7.getInt(0) + array7.getInt(1));
StringReader reader8 = new StringReader("{ \"foo\": 3, \"bar\": [ { \"foo\": 5, \"baz\": 4}] }");
String s8 = parseObject(reader8, toStringObjectVisitor());
System.out.println(s8);
StringReader reader9= new StringReader("{ \"year\": 2014, \"amounts\": [ { \"amount\": 3 }, { \"amount\": 4 }, { \"amount\": 4 } ] }");
JsonObject object9 = parseObject(reader9,
jsonObjectVisitor(Function.identity(),
null,
arrayVisitor(stream -> stream.map(JsonObject.class::cast).mapToInt(o -> o.getInt("amount")).sum(),
jsonObjectVisitor(),
null)));
System.out.println(object9.asMap());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment