added support for lists (no maps as list elements yet)

This commit is contained in:
Sander Hautvast 2020-07-07 21:12:04 +02:00
parent e1874de683
commit b72935960f
4 changed files with 166 additions and 12 deletions

View file

@ -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();
} }
@ -152,6 +201,7 @@ 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);
} }
@ -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;
}
} }

View file

@ -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());
} }
} }

View 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();
}
}

View 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);
}
}