added support for lists (no maps as list elements yet)
This commit is contained in:
parent
e1874de683
commit
b72935960f
4 changed files with 166 additions and 12 deletions
|
|
@ -1,18 +1,22 @@
|
||||||
package nl.sander.jsontoy2;
|
package nl.sander.jsontoy2;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public class IoReader {
|
public class IoReader {
|
||||||
private static final int HEX_RADIX = 16;
|
private static final int HEX_RADIX = 16;
|
||||||
private final InputStream inputStream;
|
private final InputStream inputStream;
|
||||||
private final ByteBuf characterBuffer = new ByteBuf();
|
private final ByteBuf characterBuffer = new ByteBuf();
|
||||||
private final ByteBuf encodedCodePointBuffer = new ByteBuf(4);
|
private final ByteBuf encodedCodePointBuffer = new ByteBuf(4);
|
||||||
|
private final ByteBuffer convertBuffer = ByteBuffer.allocate(4);
|
||||||
private boolean escaping = false;
|
private boolean escaping = false;
|
||||||
private boolean encoded = false;
|
private boolean encoded = false;
|
||||||
private final ByteBuffer convertBuffer = ByteBuffer.allocate(4);
|
|
||||||
private byte current;
|
private byte current;
|
||||||
private int linecount = 0;
|
private int linecount = 0;
|
||||||
|
private long charcount = 0;
|
||||||
|
|
||||||
protected IoReader(InputStream inputStream) {
|
protected IoReader(InputStream inputStream) {
|
||||||
this.inputStream = inputStream;
|
this.inputStream = inputStream;
|
||||||
|
|
@ -55,9 +59,9 @@ public class IoReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Character readCharacter() {
|
public Character readCharacter() {
|
||||||
eat('\"');
|
eatUntil('\"');
|
||||||
char currentChar = (char) current;
|
char currentChar = (char) current;
|
||||||
eat('\"');
|
eatUntil('\"');
|
||||||
return currentChar;
|
return currentChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,9 +92,57 @@ public class IoReader {
|
||||||
return characterBuffer.toString();
|
return characterBuffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<?> readList() {
|
||||||
|
List<Object> list = new ArrayList<>();
|
||||||
|
|
||||||
|
if (current != '[') {
|
||||||
|
throw new JsonReadException("no list found");
|
||||||
|
}
|
||||||
|
advance();
|
||||||
|
while (current != -1 && current != ']') {
|
||||||
|
Optional<Object> maybeValue = readValue();
|
||||||
|
if (maybeValue.isEmpty()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
list.add(maybeValue.get());
|
||||||
|
eatUntilAny(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Object> readValue() {
|
||||||
|
Object value;
|
||||||
|
skipWhitespace();
|
||||||
|
if (current == ']') {
|
||||||
|
return Optional.empty();
|
||||||
|
} else if (current == '[') {
|
||||||
|
value = readList();
|
||||||
|
} else if (current == '{') {
|
||||||
|
value = readMap();
|
||||||
|
} else if (current == '\"') {
|
||||||
|
value = readString();
|
||||||
|
} else if (current == 'T' || current == 't' || current == 'F' || current == 'f') {
|
||||||
|
value = readBoolean();
|
||||||
|
} else {
|
||||||
|
String numeric = readNumeric();
|
||||||
|
double doubleValue = Double.parseDouble(numeric);
|
||||||
|
if ((int) doubleValue == doubleValue) {
|
||||||
|
value = (int) doubleValue;
|
||||||
|
} else {
|
||||||
|
value = doubleValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.of(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<?, ?> readMap() {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
public String readString() {
|
public String readString() {
|
||||||
eat('\"');
|
eatUntil('\"');
|
||||||
|
|
||||||
characterBuffer.clear();
|
characterBuffer.clear();
|
||||||
boolean endOfString = false;
|
boolean endOfString = false;
|
||||||
|
|
@ -134,9 +186,6 @@ public class IoReader {
|
||||||
}
|
}
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
if (current != -1) {
|
|
||||||
advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
return characterBuffer.toString();
|
return characterBuffer.toString();
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +200,8 @@ public class IoReader {
|
||||||
|
|
||||||
void advance() {
|
void advance() {
|
||||||
try {
|
try {
|
||||||
current = (byte)inputStream.read();
|
current = (byte) inputStream.read();
|
||||||
|
System.out.println((charcount++) + ":" + (char) current);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +210,7 @@ public class IoReader {
|
||||||
void skipWhitespace() {
|
void skipWhitespace() {
|
||||||
try {
|
try {
|
||||||
while (current > -1 && Character.isWhitespace(current)) {
|
while (current > -1 && Character.isWhitespace(current)) {
|
||||||
current = (byte)inputStream.read();
|
current = (byte) inputStream.read();
|
||||||
}
|
}
|
||||||
if (current == -1) {
|
if (current == -1) {
|
||||||
throw new JsonReadException("end of source reached");
|
throw new JsonReadException("end of source reached");
|
||||||
|
|
@ -170,7 +220,7 @@ public class IoReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String eat(char until) {
|
String eatUntil(char until) {
|
||||||
characterBuffer.clear();
|
characterBuffer.clear();
|
||||||
|
|
||||||
while (current > -1 && (current != until | Character.isWhitespace(current))) {
|
while (current > -1 && (current != until | Character.isWhitespace(current))) {
|
||||||
|
|
@ -180,4 +230,24 @@ public class IoReader {
|
||||||
advance();
|
advance();
|
||||||
return characterBuffer.toString();
|
return characterBuffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String eatUntilAny(char... untilOrrChars) {
|
||||||
|
characterBuffer.clear();
|
||||||
|
|
||||||
|
while (current > -1 && (!contains(untilOrrChars, current) | Character.isWhitespace(current))) {
|
||||||
|
characterBuffer.add(current);
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
advance();
|
||||||
|
return characterBuffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean contains(char[] chars, byte search) {
|
||||||
|
for (char aChar : chars) {
|
||||||
|
if (search == aChar) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import nl.sander.jsontoy2.readers.*;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
|
@ -32,7 +33,7 @@ public class JsonReader {
|
||||||
return readers.get(type);
|
return readers.get(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> void register(Class<T> type, JsonObjectReader<T> objectReader) {
|
static <T> void register(Class<T> type, JsonObjectReader<T> objectReader) {
|
||||||
readers.put(type, objectReader);
|
readers.put(type, objectReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,5 +57,6 @@ public class JsonReader {
|
||||||
register(char.class, new CharReader());
|
register(char.class, new CharReader());
|
||||||
register(String.class, new StringReader());
|
register(String.class, new StringReader());
|
||||||
register(LocalDateTime.class, new LocalDateTimeReader());
|
register(LocalDateTime.class, new LocalDateTimeReader());
|
||||||
|
register(List.class, new ListReader());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
src/main/java/nl/sander/jsontoy2/readers/ListReader.java
Normal file
14
src/main/java/nl/sander/jsontoy2/readers/ListReader.java
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
package nl.sander.jsontoy2.readers;
|
||||||
|
|
||||||
|
import nl.sander.jsontoy2.IoReader;
|
||||||
|
import nl.sander.jsontoy2.JsonObjectReader;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public class ListReader implements JsonObjectReader<List> {
|
||||||
|
@Override
|
||||||
|
public List<?> read(IoReader ioReader) {
|
||||||
|
return ioReader.readList();
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/test/java/nl/sander/jsontoy2/Lists.java
Normal file
68
src/test/java/nl/sander/jsontoy2/Lists.java
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
package nl.sander.jsontoy2;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public class Lists {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void emptyList() {
|
||||||
|
List<String> list = JsonReader.read(List.class, "[]");
|
||||||
|
assertEquals(new ArrayList<>(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void singleStringList() {
|
||||||
|
List<String> list = JsonReader.read(List.class, "[\"hello jason\"]");
|
||||||
|
assertEquals(Collections.singletonList("hello jason"), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleStringList() {
|
||||||
|
List<String> list = JsonReader.read(List.class, "[\"hello\" , \"jason\"]");
|
||||||
|
List<String> expected = Arrays.asList("hello", "jason");
|
||||||
|
assertEquals(expected, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleStrings_noComma_error() {
|
||||||
|
List<String> list = JsonReader.read(List.class, "[\"hello\" \"jason\"]");
|
||||||
|
List<String> expected = Collections.singletonList("hello");
|
||||||
|
assertEquals(expected, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void singleInt() {
|
||||||
|
List<Integer> list = JsonReader.read(List.class, "[1]");
|
||||||
|
List<Integer> expected = Collections.singletonList(1);
|
||||||
|
assertEquals(expected, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleInts() {
|
||||||
|
List<Integer> list = JsonReader.read(List.class, "[1,2]");
|
||||||
|
List<Integer> expected = Arrays.asList(1,2);
|
||||||
|
assertEquals(expected, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void intDoubleBooleanString() {
|
||||||
|
List<Integer> list = JsonReader.read(List.class, "[1,2.5,false,\"hello jason\"]");
|
||||||
|
List<?> expected = Arrays.asList(1,2.5,false,"hello jason");
|
||||||
|
assertEquals(expected, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nestedList() {
|
||||||
|
List<Integer> list = JsonReader.read(List.class, "[[],[]]");
|
||||||
|
List<?> expected = Arrays.asList(List.of(), List.of());
|
||||||
|
assertEquals(expected, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue