Skip to content

Commit

Permalink
Update JSONDecoder.
Browse files Browse the repository at this point in the history
  • Loading branch information
gk-brown committed Oct 14, 2024
1 parent 4d6119e commit 5ad428e
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
24 changes: 24 additions & 0 deletions kilo-client/src/main/java/org/httprpc/kilo/io/JSONDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Deque;
Expand All @@ -31,6 +32,7 @@
*/
public class JSONDecoder extends Decoder<Object> {
private Type type;
private Type elementType;

private int c = EOF;

Expand Down Expand Up @@ -61,6 +63,12 @@ public JSONDecoder(Type type) {
}

this.type = type;

if (type instanceof ParameterizedType parameterizedType && parameterizedType.getRawType() == List.class) {
elementType = parameterizedType.getActualTypeArguments()[0];
} else {
elementType = null;
}
}

@Override
Expand All @@ -82,6 +90,10 @@ public Object read(Reader reader) throws IOException {
if (c == ']' || c == '}') {
value = containers.pop();

if (elementType != null && containers.size() == 1 && containers.peek() instanceof List<?> list) {
((List<Object>)list).set(list.size() - 1, BeanAdapter.toGenericType(value, elementType));
}

c = reader.read();
} else if (c == ',') {
c = reader.read();
Expand Down Expand Up @@ -148,6 +160,10 @@ public Object read(Reader reader) throws IOException {
if (key != null) {
((Map<String, Object>)container).put(key, value);
} else {
if (elementType != null && containers.size() == 1) {
value = BeanAdapter.toGenericType(value, elementType);
}

((List<Object>)container).add(value);
}
}
Expand All @@ -160,6 +176,14 @@ public Object read(Reader reader) throws IOException {
throw new IOException("Unterminated container.");
}

if (elementType != null) {
if (!(value instanceof List<?>)) {
throw new IOException("Value is not a list.");
}

return value;
}

return BeanAdapter.toGenericType(value, type);
}

Expand Down
80 changes: 80 additions & 0 deletions kilo-client/src/test/java/org/httprpc/kilo/io/JSONDecoderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@

package org.httprpc.kilo.io;

import org.httprpc.kilo.beans.BeanAdapter;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.function.Supplier;

import static org.httprpc.kilo.util.Collections.*;
Expand All @@ -30,6 +34,29 @@ public interface Row {
boolean isC();
}

private static class ListType implements ParameterizedType {
Type[] actualTypeArguments;

ListType(Type elementType) {
actualTypeArguments = new Type[] {elementType};
}

@Override
public Type[] getActualTypeArguments() {
return actualTypeArguments;
}

@Override
public Type getRawType() {
return List.class;
}

@Override
public Type getOwnerType() {
return null;
}
}

@Test
public void testString() throws IOException {
assertEquals("abcdéfg", decode("\"abcdéfg\""));
Expand Down Expand Up @@ -86,12 +113,65 @@ public void testArray() throws IOException {
assertEquals(expected, actual);
}

@Test
public void testRowArray() throws IOException {
var expected = BeanAdapter.coerceList(listOf(
mapOf(
entry("a", "hello"),
entry("b", 123),
entry("c", true)
),
mapOf(
entry("a", "goodbye"),
entry("b", 456),
entry("c", false)
)
), Row.class);

var text = "[{\"a\": \"hello\", \"b\": 123, \"c\": true}, {\"a\": \"goodbye\", \"b\": 456, \"c\": false}]";

var actual = decode(text, () -> new JSONDecoder(new ListType(Row.class)));

assertEquals(expected, actual);
}

@Test
public void testStringArray() throws IOException {
var expected = listOf("1", "2", "3");

var text = "[1, 2, 3]";

var actual = decode(text, () -> new JSONDecoder(new ListType(String.class)));

assertEquals(expected, actual);
}

@Test
public void testNestedArray() throws IOException {
var expected = listOf(
listOf(1),
listOf(2, 3),
listOf(4, 5, 6)
);

var text = "[[1], [2, 3], [4, 5, 6]]";

var actual = decode(text, () -> new JSONDecoder(new ListType(new ListType(Integer.class))));

assertEquals(expected, actual);
}

@Test
public void testUnterminatedArray() {
assertThrows(IOException.class, () -> decode("[1, 2, 3"));
assertThrows(IOException.class, () -> decode("[1, 2, 3, "));
}

@Test
public void testInvalidArray() {
assertThrows(IOException.class, () -> decode("abc", () -> new JSONDecoder(new ListType(Double.class))));
}

@Test
public void testObject() throws IOException {
var expected = mapOf(
Expand Down

0 comments on commit 5ad428e

Please sign in to comment.