added support for maps

This commit is contained in:
Sander Hautvast 2020-07-08 18:21:26 +02:00
parent b72935960f
commit 52def01e5f
21 changed files with 298 additions and 90 deletions

87
pom.xml Normal file
View file

@ -0,0 +1,87 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>JsonToy2</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
<groupId>nl.sander</groupId>
<artifactId>jsontoy2</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.26.0-GA</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>1.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -16,7 +16,6 @@ public class IoReader {
private boolean encoded = false; private boolean encoded = false;
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;
@ -93,11 +92,11 @@ public class IoReader {
} }
public List<?> readList() { public List<?> readList() {
List<Object> list = new ArrayList<>(); skipWhitespace();
if (current != '[') { if (current != '[') {
throw new JsonReadException("no list found"); throw new JsonReadException("no list found");
} }
List<Object> list = new ArrayList<>();
advance(); advance();
while (current != -1 && current != ']') { while (current != -1 && current != ']') {
Optional<Object> maybeValue = readValue(); Optional<Object> maybeValue = readValue();
@ -105,17 +104,39 @@ public class IoReader {
break; break;
} else { } else {
list.add(maybeValue.get()); list.add(maybeValue.get());
eatUntilAny(','); eatUntil(',');
} }
} }
return list; return list;
} }
public Map<?, ?> readMap() {
HashMap<Object, Object> map = new HashMap<>();
skipWhitespace();
if (current != '{') {
throw new JsonReadException("no map found");
}
while (current != -1 && current != '}') {
skipWhitespace();
if (current == '"') {
String key = readString();
eatUntil(':');
skipWhitespace();
Optional<Object> maybeValue = readValue();
maybeValue.ifPresent(o -> map.put(key, o));
eatUntil(',');
} else {
advance();
}
}
return map;
}
private Optional<Object> readValue() { private Optional<Object> readValue() {
Object value; Object value;
skipWhitespace(); skipWhitespace();
if (current == ']') { if (current == ']' || current == '}') {
return Optional.empty(); return Optional.empty();
} else if (current == '[') { } else if (current == '[') {
value = readList(); value = readList();
@ -137,10 +158,6 @@ public class IoReader {
return Optional.of(value); return Optional.of(value);
} }
private Map<?, ?> readMap() {
return new HashMap<>();
}
public String readString() { public String readString() {
eatUntil('\"'); eatUntil('\"');
@ -158,7 +175,7 @@ public class IoReader {
endOfString = true; endOfString = true;
} }
} else { } else {
// json encoded string // unicode codepoint
if (current == 'u') { if (current == 'u') {
encoded = true; encoded = true;
} else if (current == 'n') { } else if (current == 'n') {
@ -172,7 +189,7 @@ public class IoReader {
throw new JsonReadException("illegal escaped quote in line " + linecount); throw new JsonReadException("illegal escaped quote in line " + linecount);
} else { } else {
if (encoded) { if (encoded) {
// load next 4 characters in special buffer // load next 4 characters in special buffer to convert to int
encodedCodePointBuffer.add(current); encodedCodePointBuffer.add(current);
if (encodedCodePointBuffer.length() == 4) { if (encodedCodePointBuffer.length() == 4) {
byte[] bytes = parseCodePoint(); byte[] bytes = parseCodePoint();
@ -201,7 +218,6 @@ 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);
} }
@ -230,24 +246,4 @@ 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

@ -6,6 +6,7 @@ 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.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -13,7 +14,7 @@ import java.util.concurrent.ConcurrentMap;
* public facade * public facade
*/ */
public class JsonReader { public class JsonReader {
private static final ConcurrentMap<Class<?>, JsonObjectReader<?>> readers = new ConcurrentHashMap<>(); private static final ConcurrentMap<Class<?>, JsonValueReader<?>> readers = new ConcurrentHashMap<>();
public static <T> T read(Class<T> type, InputStream reader) { public static <T> T read(Class<T> type, InputStream reader) {
return read(type, new IoReader(reader)); return read(type, new IoReader(reader));
@ -29,11 +30,11 @@ public class JsonReader {
// class.cast() does not work for primitives; // class.cast() does not work for primitives;
} }
private static <T> JsonObjectReader<?> getReader(Class<T> type) { private static <T> JsonValueReader<?> getReader(Class<T> type) {
return readers.get(type); return readers.get(type);
} }
static <T> void register(Class<T> type, JsonObjectReader<T> objectReader) { static <T> void register(Class<T> type, JsonValueReader<T> objectReader) {
readers.put(type, objectReader); readers.put(type, objectReader);
} }
@ -58,5 +59,6 @@ public class JsonReader {
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()); register(List.class, new ListReader());
register(Map.class, new MapReader());
} }
} }

View file

@ -2,6 +2,6 @@ package nl.sander.jsontoy2;
import java.io.Reader; import java.io.Reader;
public interface JsonObjectReader<T> { public interface JsonValueReader<T> {
T read(IoReader ioReader); T read(IoReader ioReader);
} }

View file

@ -1,8 +1,9 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonValueReader;
public class BooleanReader implements nl.sander.jsontoy2.JsonObjectReader<Boolean> { public class BooleanReader implements JsonValueReader<Boolean> {
@Override @Override
public Boolean read(IoReader ioReader) { public Boolean read(IoReader ioReader) {
return ioReader.readBoolean(); return ioReader.readBoolean();

View file

@ -1,9 +1,9 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
public class ByteReader implements JsonObjectReader<Byte> { public class ByteReader implements JsonValueReader<Byte> {
@Override @Override
public Byte read(IoReader ioReader) { public Byte read(IoReader ioReader) {
return ioReader.readByte(); return ioReader.readByte();

View file

@ -1,9 +1,9 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
public class CharReader implements JsonObjectReader<Character> { public class CharReader implements JsonValueReader<Character> {
@Override @Override
public Character read(IoReader ioReader) { public Character read(IoReader ioReader) {
return ioReader.readCharacter(); return ioReader.readCharacter();

View file

@ -1,17 +1,12 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Date; import java.util.Date;
public class DateReader extends AbstractDatesReader<Date> implements JsonObjectReader<Date> { public class DateReader extends AbstractDatesReader<Date> implements JsonValueReader<Date> {
@Override @Override
public Date read(IoReader ioReader) { public Date read(IoReader ioReader) {

View file

@ -1,11 +1,9 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
import java.io.Reader; public class DoubleReader implements JsonValueReader<Double> {
public class DoubleReader implements JsonObjectReader<Double> {
@Override @Override
public Double read(IoReader ioReader) { public Double read(IoReader ioReader) {
return ioReader.readDouble(); return ioReader.readDouble();

View file

@ -1,9 +1,9 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
public class FloatReader implements JsonObjectReader<Float> { public class FloatReader implements JsonValueReader<Float> {
@Override @Override
public Float read(IoReader ioReader) { public Float read(IoReader ioReader) {
return ioReader.readFloat(); return ioReader.readFloat();

View file

@ -1,9 +1,9 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
public class IntegerReader implements JsonObjectReader<Integer> { public class IntegerReader implements JsonValueReader<Integer> {
@Override @Override
public Integer read(IoReader ioReader) { public Integer read(IoReader ioReader) {
return ioReader.readInteger(); return ioReader.readInteger();

View file

@ -1,12 +1,12 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
import java.util.List; import java.util.List;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class ListReader implements JsonObjectReader<List> { public class ListReader implements JsonValueReader<List> {
@Override @Override
public List<?> read(IoReader ioReader) { public List<?> read(IoReader ioReader) {
return ioReader.readList(); return ioReader.readList();

View file

@ -1,12 +1,12 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
public class LocalDateTimeReader extends AbstractDatesReader<LocalDateTime> implements JsonObjectReader<LocalDateTime> { public class LocalDateTimeReader extends AbstractDatesReader<LocalDateTime> implements JsonValueReader<LocalDateTime> {
@Override @Override
public LocalDateTime read(IoReader ioReader) { public LocalDateTime read(IoReader ioReader) {

View file

@ -1,9 +1,9 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
public class LongReader implements JsonObjectReader<Long> { public class LongReader implements JsonValueReader<Long> {
@Override @Override
public Long read(IoReader ioReader) { public Long read(IoReader ioReader) {
return ioReader.readLong(); return ioReader.readLong();

View file

@ -0,0 +1,13 @@
package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonValueReader;
import java.util.Map;
public class MapReader implements JsonValueReader<Map> {
@Override
public Map read(IoReader ioReader) {
return ioReader.readMap();
}
}

View file

@ -1,9 +1,9 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
public class ShortReader implements JsonObjectReader<Short> { public class ShortReader implements JsonValueReader<Short> {
@Override @Override
public Short read(IoReader ioReader) { public Short read(IoReader ioReader) {

View file

@ -1,9 +1,9 @@
package nl.sander.jsontoy2.readers; package nl.sander.jsontoy2.readers;
import nl.sander.jsontoy2.IoReader; import nl.sander.jsontoy2.IoReader;
import nl.sander.jsontoy2.JsonObjectReader; import nl.sander.jsontoy2.JsonValueReader;
public class StringReader implements JsonObjectReader<String> { public class StringReader implements JsonValueReader<String> {
@Override @Override
public String read(IoReader ioReader) { public String read(IoReader ioReader) {

View file

@ -2,10 +2,7 @@ package nl.sander.jsontoy2;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ -15,54 +12,61 @@ public class Lists {
@Test @Test
public void emptyList() { public void emptyList() {
List<String> list = JsonReader.read(List.class, "[]"); List<String> list = JsonReader.read(List.class, "[]");
assertEquals(new ArrayList<>(), list); assertEquals(List.of(), list);
} }
@Test @Test
public void singleStringList() { public void singleStringList() {
List<String> list = JsonReader.read(List.class, "[\"hello jason\"]"); List<String> list = JsonReader.read(List.class, "[\"hello jason\"]");
assertEquals(Collections.singletonList("hello jason"), list); assertEquals(List.of("hello jason"), list);
} }
@Test @Test
public void multipleStringList() { public void multipleStringList() {
List<String> list = JsonReader.read(List.class, "[\"hello\" , \"jason\"]"); List<String> list = JsonReader.read(List.class, "[\"hello\" , \"jason\"]");
List<String> expected = Arrays.asList("hello", "jason"); List<String> expected = List.of("hello", "jason");
assertEquals(expected, list); assertEquals(expected, list);
} }
@Test @Test
public void multipleStrings_noComma_error() { public void multipleStrings_noComma_error() {
List<String> list = JsonReader.read(List.class, " [\"hello\" \"jason\"]"); List<String> list = JsonReader.read(List.class, " [\"hello\" \"jason\"]");
List<String> expected = Collections.singletonList("hello"); List<String> expected = List.of("hello");
assertEquals(expected, list); assertEquals(expected, list);
} }
@Test @Test
public void singleInt() { public void singleInt() {
List<Integer> list = JsonReader.read(List.class, " [ 1 ]"); List<Integer> list = JsonReader.read(List.class, " [ 1 ]");
List<Integer> expected = Collections.singletonList(1); List<Integer> expected = List.of(1);
assertEquals(expected, list); assertEquals(expected, list);
} }
@Test @Test
public void multipleInts() { public void multipleInts() {
List<Integer> list = JsonReader.read(List.class, "[1,2]"); List<Integer> list = JsonReader.read(List.class, "[1,2]");
List<Integer> expected = Arrays.asList(1,2); List<Integer> expected = List.of(1, 2);
assertEquals(expected, list); assertEquals(expected, list);
} }
@Test @Test
public void intDoubleBooleanString() { public void intDoubleBooleanString() {
List<Integer> list = JsonReader.read(List.class, "[1, 2.5,false, \"hello jason\"]"); List<Integer> list = JsonReader.read(List.class, "[1, 2.5,false, \"hello jason\"]");
List<?> expected = Arrays.asList(1,2.5,false,"hello jason"); List<?> expected = List.of(1, 2.5, false, "hello jason");
assertEquals(expected, list); assertEquals(expected, list);
} }
@Test @Test
public void nestedList() { public void nestedList() {
List<Integer> list = JsonReader.read(List.class, "[[],[]]"); List<Integer> list = JsonReader.read(List.class, "[[],[]]");
List<?> expected = Arrays.asList(List.of(), List.of()); List<?> expected = List.of(List.of(), List.of());
assertEquals(expected, list);
}
@Test
public void mapInList() {
List<Object> list = JsonReader.read(List.class, "[[],{\"list\":[]]}]");
List<?> expected = List.of(List.of(), Map.of("list",List.of()));
assertEquals(expected, list); assertEquals(expected, list);
} }
} }

View file

@ -0,0 +1,66 @@
package nl.sander.jsontoy2;
import org.junit.jupiter.api.Test;
import java.util.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@SuppressWarnings("unchecked")
public class Maps {
@Test
public void emptyMap() {
Map<Object, Object> map = JsonReader.read(Map.class, "{}");
assertEquals(new HashMap<>(), map);
}
@Test
public void singleStringKeyValueMap() {
Map<String, String> map = JsonReader.read(Map.class, "{\"message\": \"hello jason\"}");
assertEquals(Collections.singletonMap("message", "hello jason"), map);
}
@Test
public void multipleValues() {
Map<String, Object> map = JsonReader.read(Map.class, "{\"value1\" : \"jason\" ,\n \"value2\":1}");
Map<String, Object> expected = new HashMap<>();
expected.put("value1", "jason");
expected.put("value2", 1);
assertEquals(expected, map);
}
@Test
public void multipleStrings_noColon_error() {
assertThrows(JsonReadException.class, () -> JsonReader.read(Map.class, " {\"hello\" \"jason\"}"));
}
@Test
public void singleInts() {
Map<String,Integer> map = JsonReader.read(Map.class, " { \"1\":2 }");
Map<String,Integer> expected = Collections.singletonMap("1",2);
assertEquals(expected, map);
}
@Test
@SuppressWarnings("raw")
public void nestedMap() {
Map<String,Map> list = JsonReader.read(Map.class, "{\"map\": {\"map\":{}}}");
Map<String,Map> expected = new HashMap<>();
HashMap<String,Map> n1 = new HashMap<>();
n1.put("map",new HashMap<>());
expected.put("map", n1);
assertEquals(expected, list);
}
@Test
public void listInMap(){
Map<String,List> list = JsonReader.read(Map.class, " { \"list\" : [ 1 ] } ");
Map<String,List> expected = new HashMap<>();
expected.put("list", List.of(1));
assertEquals(expected, list);
}
}

View file

@ -2,12 +2,7 @@ package nl.sander.jsontoy2;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException; import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
@ -48,7 +43,7 @@ public class Strings {
@Test @Test
public void escapedSingleQuote() { public void escapedSingleQuote() {
assertThrows(JsonReadException.class, () -> JsonReader.read(String.class, "\"\\\'\"")); assertThrows(JsonReadException.class, () -> JsonReader.read(String.class, "\"\\'\""));
} }
} }

View file

@ -0,0 +1,51 @@
package nl.sander.jsontoy2;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.nio.charset.CharacterCodingException;
import static org.junit.jupiter.api.Assertions.*;
public class StringsWithJackson {
ObjectMapper jackson = new ObjectMapper();
@Test
public void regular() throws JsonProcessingException {
assertEquals("DADA", jackson.readValue("\"DADA\"", String.class));
}
@Test
public void firstSurrogateButSecondMissing() throws NoSuchFieldException, IllegalAccessException, JsonProcessingException {
String value =jackson.readValue("\"\\uDADA\"", String.class);
assertTrue(true);
}
@Test
public void incompleteSurrogateAndEscapeValid() throws JsonProcessingException {
assertThrows(JsonParseException.class, () -> jackson.readValue("\"\\uD800\n\"", String.class));
}
@Test
public void firstValidSurrogateSecondInvalid() throws JsonProcessingException {
String value =jackson.readValue("\"\\uD888\\u1334\"", String.class);
assertTrue(true);
}
@Test
public void escapedDoubleQuote() throws JsonProcessingException {
String value =jackson.readValue("\"\\\"\"", String.class);
assertEquals("\"", value);
}
@Test
public void escapedSingleQuote() {
assertThrows(JsonParseException.class, () -> jackson.readValue("\"\\'\"", String.class));
}
}