Created
July 31, 2014 00:16
-
-
Save forax/81a56cf2684bfa2e46ec to your computer and use it in GitHub Desktop.
Json parser 'modern' API in one file
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
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