JsonArrayStreamDataSupplier.java
/**
* @license APACHE LICENSE, VERSION 2.0 http://www.apache.org/licenses/LICENSE-2.0
* @author Michael Witbrock
* https://github.com/witbrock/JacksonStream/blob/master/src/main/java/com/michaelwitbrock/jacksonstream/JsonArrayStreamDataSupplier.java
*/
package jasper.util;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class JsonArrayStreamDataSupplier<T> implements Iterator<T> {
/*
* This class wraps the Jackson streaming API for arrays (a common kind of
* large JSON file) in a Java 8 Stream. The initial motivation was that
* use of a default objectmapper to a Java array was crashing for me on
* a very large JSON file (> 1GB). And there didn't seem to be good example
* code for handling Jackson streams as Java 8 streams, which seems natural.
*/
ObjectMapper mapper;
JsonParser parser;
boolean maybeHasNext = false;
int count = 0;
JsonFactory factory = new JsonFactory();
private Class<T> type;
public JsonArrayStreamDataSupplier(InputStream in, Class<T> type, ObjectMapper mapper) {
this.type = type;
this.mapper = mapper;
try {
// Setup and get into a state to start iterating
parser = factory.createParser(in);
parser.setCodec(mapper);
JsonToken token = parser.nextToken();
if (token == null) {
throw new RuntimeException("Can't get any JSON Token");
}
// the first token is supposed to be the start of array '['
if (!JsonToken.START_ARRAY.equals(token)) {
// return or throw exception
maybeHasNext = false;
throw new RuntimeException("Can't get any JSON Token from array start");
}
} catch (Exception e) {
maybeHasNext = false;
}
maybeHasNext = true;
}
/*
This method returns the stream, and is the only method other
than the constructor that should be used.
*/
public Stream<T> getStream() {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(this, 0), false);
}
/* The remaining methods are what enables this to be passed to the spliterator generator,
since they make it Iterable.
*/
@Override
public boolean hasNext() {
if (!maybeHasNext) {
return false; // didn't get started
}
try {
return (parser.nextToken() == JsonToken.START_OBJECT);
} catch (Exception e) {
System.out.println("Ex" + e);
return false;
}
}
@Override
public T next() {
try {
JsonNode n = parser.readValueAsTree();
//Because we can't send T as a parameter to the mapper
T node = mapper.convertValue(n, type);
return node;
} catch (IOException | IllegalArgumentException e) {
System.out.println("Ex" + e);
return null;
}
}
}