diff --git a/pom.xml b/pom.xml index dea87ca..307f89c 100644 --- a/pom.xml +++ b/pom.xml @@ -59,9 +59,9 @@ - junit - junit - 4.13 + org.junit.jupiter + junit-jupiter-engine + 5.6.2 test diff --git a/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java b/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java new file mode 100644 index 0000000..5559c64 --- /dev/null +++ b/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java @@ -0,0 +1,153 @@ +package nl.sander.jsontoy2; + +import javassist.*; +import nl.sander.jsontoy2.javassist.ClassCreationException; +import nl.sander.jsontoy2.javassist.Javassist; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class JavaObjectReaderFactory { + public static final String ROOT_PACKAGE = "serializer."; + private final static CtClass DESERIALIZER_BASE = Javassist.getTypeDefinition(JsonValueReader.class); + private static final CtClass[] NO_EXCEPTIONS = {}; + private static final CtClass[] PARSER_PARAM = {Javassist.getTypeDefinition(Parser.class)}; + private static final Class[] NO_PARAMS = {}; + private static final CtClass[] CT_NO_PARAMS = {}; + private final static CtClass OBJECT_CLASS = Javassist.getTypeDefinition(Object.class); + private final static CtClass BOOLEAN = Javassist.getTypeDefinition(boolean.class); + private final static CtClass INT = Javassist.getTypeDefinition(int.class); + private final static CtClass LONG = Javassist.getTypeDefinition(long.class); + private final static CtClass BYTE = Javassist.getTypeDefinition(byte.class); + private final static CtClass SHORT = Javassist.getTypeDefinition(short.class); + private final static CtClass FLOAT = Javassist.getTypeDefinition(float.class); + private final static CtClass DOUBLE = Javassist.getTypeDefinition(double.class); + + @SuppressWarnings("unchecked") + static JsonValueReader createReaderInstance(Class type) { + try { + Class jsonTypeReader = createReaderClass(type); + return (JsonValueReader) jsonTypeReader.getDeclaredConstructor(NO_PARAMS).newInstance(new Object[]{}); + + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new JsonParseException(e); + } + } + + private static Class createReaderClass(Class type) { + String name = createDeserializerName(type); + CtClass deserializerClass = Javassist.createClass(name, DESERIALIZER_BASE); + + try { + deserializerClass.addConstructor(CtNewConstructor.make(CT_NO_PARAMS, NO_EXCEPTIONS, "{super();}", deserializerClass)); + deserializerClass.addMethod(createReadJsonMethod(deserializerClass, type)); + return deserializerClass.toClass(); + } catch (CannotCompileException e) { + throw new ClassCreationException(e); + } + } + + private static CtMethod createReadJsonMethod(CtClass serializerClass, Class type) { + try { + String readMethodBodySource = createReadMethodBodySource(type); + System.out.println(readMethodBodySource); + return CtNewMethod.make(Modifier.PUBLIC, OBJECT_CLASS, "read", PARSER_PARAM, NO_EXCEPTIONS, readMethodBodySource, serializerClass); + } catch (CannotCompileException e) { + throw new ClassCreationException(e); + } + } + + + private static String createReadMethodBodySource(Class type) { + String source = "{"; + String typeName = type.getName(); + if (ReaderFactory.readerSuppliers.containsKey(type)) { + source += "return " + JsonReader.class.getName() + ".read(" + typeName + ".class, $1);"; + } else { + source += "java.util.Map object=" + JsonReader.class.getName() + ".readJavaObject(" + typeName + ".class,$1);\n"; + source += typeName + " instance = new " + typeName + "();\n"; + + for (Field field : type.getDeclaredFields()) { + source += "instance.set" + capitalize(field.getName()) + + "(" + getSourceForGetValueFromObject(field) + ");\n"; + } + + source += "return instance;"; + } + + source += "}\n"; + return source; + } + + @NotNull + private static String getSourceForGetValueFromObject(Field field) { + Class fieldType = field.getType(); + String fieldName = field.getName(); + if (fieldType == boolean.class) { + return "getBoolean(\"" + fieldName + "\", object)"; + } else if (fieldType == int.class) { + return "getInt(\"" + fieldName + "\", object)"; + } else if (fieldType == long.class) { + return "getLong(\"" + fieldName + "\",object)"; + } else if (fieldType == short.class) { + return "getShort(\"" + fieldName + "\",object)"; + } else if (fieldType == byte.class) { + return "getByte(\"" + fieldName + "\",object)"; + } else if (fieldType == float.class) { + return "getFloat(\"" + fieldName + "\",object)"; + } else if (fieldType == double.class) { + return "getDouble(\"" + fieldName + "\",object)"; + } else if (Set.class.isAssignableFrom(fieldType)) { + return "getSet(\"" + fieldName + "\",object)"; + } else { + return "(" + fieldType.getName() + ")(object.get(\"" + fieldName + "\"))"; + } + } + + + private static String genericType(CtField field) { + try { + if (!Javassist.isCollection(field.getType())) { + return ""; + } else { + + String genericSignature = field.getGenericSignature(); // java.util.List; + Pattern p = Pattern.compile("(.+?)<(.+?);>;"); + Matcher matcher = p.matcher(genericSignature); + if (matcher.find()) { + String[] genericTypes = matcher.group(2).substring(1).replaceAll("/", ".").split(";L"); + for (int i = 0; i < genericTypes.length; i++) { + genericTypes[i] += ".class"; + } + return String.join(",", genericTypes); + } + throw new ClassCreationException("Generic type not ok"); + } + } catch (NotFoundException e) { + throw new ClassCreationException(e); + } + } + + private static String capitalize(String lowercase) { + return lowercase.substring(0, 1).toUpperCase() + lowercase.substring(1); + } + + private static Set createTypeList(Class... classes) { + return Arrays.stream(classes).map(Class::getName).collect(Collectors.toSet()); + } + + /* + * custom root package is prepended to avoid the java.lang class in which it's illegal to create new classes + * + * Array marks ( '[]' ) are replaced by the 'Array', Otherwise the SerializerClassName would be syntactically incorrect + */ + private static String createDeserializerName(Class type) { + return ROOT_PACKAGE + type.getName().replaceAll("\\[]", "Array") + "Deserializer"; + } +} diff --git a/src/main/java/nl/sander/jsontoy2/JsonReader.java b/src/main/java/nl/sander/jsontoy2/JsonReader.java index 8dedf14..ccd6e11 100644 --- a/src/main/java/nl/sander/jsontoy2/JsonReader.java +++ b/src/main/java/nl/sander/jsontoy2/JsonReader.java @@ -1,33 +1,21 @@ package nl.sander.jsontoy2; -import nl.sander.jsontoy2.readers.*; - import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.lang.ref.SoftReference; import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.util.Date; -import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Supplier; /** * public api */ public class JsonReader { - private static final ConcurrentMap, Supplier>> readSuppliers = new ConcurrentHashMap<>(); - private static final ConcurrentMap, JsonValueReader> readers = new ConcurrentHashMap<>(); + private final static ThreadLocal> PARSERS = new ThreadLocal<>(); - static { - registerPrimitiveTypeReaders(); - } /** * reads a value from a stream for a type that is not known beforehand @@ -43,21 +31,12 @@ public class JsonReader { * array => List */ public static Object read(InputStream inputStream) { - final InputStream in = ensureBuffered(inputStream); + final InputStream in = ensureBufferedStream(inputStream); try (Parser parser = getParser(in)) { return read(parser); } } - private static InputStream ensureBuffered(InputStream inputStream) { - if (inputStream instanceof BufferedInputStream) { - return inputStream; - } else { - return new BufferedInputStream(inputStream); - } - } - - /** * Reads a value from a string for a type that is not known beforehand * @@ -68,28 +47,6 @@ public class JsonReader { return read(getParser(jsonString)); } - private static Parser getParser(String jsonString) { - return getParser(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8))); - } - - private static Parser getParser(InputStream inputStream) { - Objects.requireNonNull(inputStream, "File not found"); - Parser parser; - SoftReference parserReference = PARSERS.get(); - if (parserReference == null || (parser = parserReference.get()) == null) { - parser = new Parser(inputStream); - parserReference = new SoftReference<>(parser); - PARSERS.set(parserReference); - } else { - parser.init(inputStream); - } - return parser; - } - - static Object read(Parser parser) { - return parser.parseAny(); - } - /** * Reads a value from a stream for the given type * @@ -117,41 +74,47 @@ public class JsonReader { return read(type, getParser(jsonString)); } + private static Parser getParser(String jsonString) { + return getParser(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8))); + } + + private static Parser getParser(InputStream inputStream) { + Objects.requireNonNull(inputStream, "File not found"); + Parser parser; + SoftReference parserReference = PARSERS.get(); + if (parserReference == null || (parser = parserReference.get()) == null) { + parser = new Parser(inputStream); + parserReference = new SoftReference<>(parser); + PARSERS.set(parserReference); + } else { + parser.init(inputStream); + } + return parser; + } + + static Object read(Parser parser) { + return parser.parseAny(); + } + @SuppressWarnings("unchecked") - private static T read(Class type, Parser parser) { - return (T) getReader(type).read(parser); + public static T read(Class type, Parser parser) { + return (T) ReaderFactory.getReader(type).read(parser); // class.cast() does not work well for primitives; } - private static JsonValueReader getReader(Class type) { - return readers.computeIfAbsent(type, k -> readSuppliers.get(k).get()); + @SuppressWarnings("unused") + public static Map readJavaObject(Class type, Parser parser) { + return parser.parseObject(type); } - private static void register(Class type, Supplier> objectReader) { - readSuppliers.put(type, objectReader); + + private static InputStream ensureBufferedStream(InputStream inputStream) { + if (inputStream instanceof BufferedInputStream) { + return inputStream; + } else { + return new BufferedInputStream(inputStream); + } } - private static void registerPrimitiveTypeReaders() { - register(Boolean.class, BooleanReader::new); - register(boolean.class, BooleanReader::new); - register(Integer.class, IntegerReader::new); - register(int.class, IntegerReader::new); - register(Long.class, LongReader::new); - register(long.class, LongReader::new); - register(Byte.class, ByteReader::new); - register(byte.class, ByteReader::new); - register(Short.class, ShortReader::new); - register(short.class, ShortReader::new); - register(Double.class, DoubleReader::new); - register(double.class, DoubleReader::new); - register(Float.class, FloatReader::new); - register(float.class, FloatReader::new); - register(Date.class, DateReader::new); - register(Character.class, CharReader::new); - register(char.class, CharReader::new); - register(String.class, StringReader::new); - register(LocalDateTime.class, LocalDateTimeReader::new); - register(List.class, ListReader::new); - register(Map.class, MapReader::new); - } + } diff --git a/src/main/java/nl/sander/jsontoy2/JsonValueReader.java b/src/main/java/nl/sander/jsontoy2/JsonValueReader.java index 7be16a5..e35eefd 100644 --- a/src/main/java/nl/sander/jsontoy2/JsonValueReader.java +++ b/src/main/java/nl/sander/jsontoy2/JsonValueReader.java @@ -1,5 +1,57 @@ package nl.sander.jsontoy2; -public interface JsonValueReader { - T read(Parser parser); +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Base class for generated readers + * + * @param the type that the reader produces + */ +@SuppressWarnings("unused") /* these methods will be called from generated code*/ +public abstract class JsonValueReader { + public abstract T read(Parser parser); + + protected boolean getBoolean(String fieldName, Map values) { + Object value = values.get(fieldName); + return value == null ? false : (Boolean) value; + } + + protected int getInt(String fieldName, Map values) { + Object value = values.get(fieldName); + return value == null ? 0 : (Integer) value; + } + + protected long getLong(String fieldName, Map values) { + Object value = values.get(fieldName); + return value == null ? 0L : (Long) value; + } + + protected short getShort(String fieldName, Map values) { + Object value = values.get(fieldName); + return value == null ? (short) 0 : (Short) value; + } + + protected byte getByte(String fieldName, Map values) { + Object value = values.get(fieldName); + return value == null ? (byte) 0 : (Byte) value; + } + + protected float getFloat(String fieldName, Map values) { + Object value = values.get(fieldName); + return value == null ? 0F : (Float) value; + } + + protected double getDouble(String fieldName, Map values) { + Object value = values.get(fieldName); + return value == null ? 0D : (Double) value; + } + + protected Set getSet(String fieldName, Map values) { + Object value = values.get(fieldName); + return value == null ? null : new HashSet<>((List) value); + } + } diff --git a/src/main/java/nl/sander/jsontoy2/Lexer.java b/src/main/java/nl/sander/jsontoy2/Lexer.java index 2985a16..0f0e3b7 100644 --- a/src/main/java/nl/sander/jsontoy2/Lexer.java +++ b/src/main/java/nl/sander/jsontoy2/Lexer.java @@ -37,6 +37,10 @@ public class Lexer implements AutoCloseable { } } + void eatUntil(char until) { + eatUntil(new char[]{until}); + } + void eatUntil(char... until) { while (current > -1 && (!contains(until, current) | Character.isWhitespace(current))) { advance(); @@ -67,4 +71,9 @@ public class Lexer implements AutoCloseable { throw new JsonParseException(e); } } + + @SuppressWarnings("unused") + public byte current() { + return current; + } } diff --git a/src/main/java/nl/sander/jsontoy2/Parser.java b/src/main/java/nl/sander/jsontoy2/Parser.java index de5f77b..f7d6517 100644 --- a/src/main/java/nl/sander/jsontoy2/Parser.java +++ b/src/main/java/nl/sander/jsontoy2/Parser.java @@ -38,7 +38,7 @@ public class Parser extends Lexer { public Integer parseInteger() { final String value = parseNumber(); - return Double.valueOf(value).intValue(); + return Integer.valueOf(value); } public Long parseLong() { @@ -125,6 +125,10 @@ public class Parser extends Lexer { } public Map parseObject() { + return parseObject(null); + } + + public Map parseObject(Class type) { final HashMap map = new HashMap<>(); skipWhitespace(); if (current != '{') { @@ -142,8 +146,13 @@ public class Parser extends Lexer { throw new JsonParseException("expected colon"); } skipWhitespace(); - final Maybe maybeValue = parseValue(); - maybeValue.ifPresent(value -> map.put(key, value)); + final Maybe maybeValue; + try { + maybeValue = type == null ? parseValue() : parseValue(type.getDeclaredField(key).getType()); + maybeValue.ifPresent(value -> map.put(key, value)); + } catch (NoSuchFieldException e) { + throw new JsonParseException(e); + } } advance(); skipWhitespace(); @@ -181,8 +190,8 @@ public class Parser extends Lexer { break; default: String numeric = parseNumber(); double doubleValue = Double.parseDouble(numeric); - if ((int) doubleValue == doubleValue) { - value = (int) doubleValue; + if ((long) doubleValue == doubleValue) { + value = (long) doubleValue; } else { value = doubleValue; } @@ -190,6 +199,20 @@ public class Parser extends Lexer { return Maybe.of(value); } + private Maybe parseValue(Class type) { + final Object value; + skipWhitespace(); + + JsonValueReader reader = ReaderFactory.getReader(type); + if (reader != null) { + value = reader.read(this); + } else { + value = parseObject(); + } + + return Maybe.of(value); + } + private Object readNull() { expect(() -> new JsonParseException("Expected 'null', encountered " + (char) current), 'n', 'u', 'l', 'l'); return null; diff --git a/src/main/java/nl/sander/jsontoy2/ReaderFactory.java b/src/main/java/nl/sander/jsontoy2/ReaderFactory.java new file mode 100644 index 0000000..29d2059 --- /dev/null +++ b/src/main/java/nl/sander/jsontoy2/ReaderFactory.java @@ -0,0 +1,66 @@ +package nl.sander.jsontoy2; + +import nl.sander.jsontoy2.readers.*; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Supplier; + +public class ReaderFactory { + static final ConcurrentMap, Supplier>> readerSuppliers = new ConcurrentHashMap<>(); + private static final ConcurrentMap, JsonValueReader> readers = new ConcurrentHashMap<>(); + private final static MapReader MAPREADER = new MapReader(); + private final static ListReader LISTREADER = new ListReader(); + + static { + registerPrimitiveTypeReaders(); + } + + public void registerCustomReader(Class type, JsonValueReader reader) { + readers.put(type, reader); + } + + static void register(Class type, Supplier> objectReader) { + readerSuppliers.put(type, objectReader); + } + + static JsonValueReader getReader(Class type) { + if (Map.class.isAssignableFrom(type)) { + return MAPREADER; + } else if (List.class.isAssignableFrom(type) || Set.class.isAssignableFrom(type)) { + return LISTREADER; + } else { + return readers.computeIfAbsent(type, k -> { + Supplier> jsonValueReaderSupplier = readerSuppliers.get(k); + + return Optional.ofNullable(jsonValueReaderSupplier) + .orElseGet(() -> () -> JavaObjectReaderFactory.createReaderInstance(type)) + .get(); + }); + } + } + + private static void registerPrimitiveTypeReaders() { + register(Boolean.class, BooleanReader::new); + register(boolean.class, BooleanReader::new); + register(Integer.class, IntegerReader::new); + register(int.class, IntegerReader::new); + register(Long.class, LongReader::new); + register(long.class, LongReader::new); + register(Byte.class, ByteReader::new); + register(byte.class, ByteReader::new); + register(Short.class, ShortReader::new); + register(short.class, ShortReader::new); + register(Double.class, DoubleReader::new); + register(double.class, DoubleReader::new); + register(Float.class, FloatReader::new); + register(float.class, FloatReader::new); + register(Date.class, DateReader::new); + register(Character.class, CharReader::new); + register(char.class, CharReader::new); + register(String.class, StringReader::new); + register(LocalDateTime.class, LocalDateTimeReader::new); + } +} diff --git a/src/main/java/nl/sander/jsontoy2/javassist/ClassCreationException.java b/src/main/java/nl/sander/jsontoy2/javassist/ClassCreationException.java new file mode 100644 index 0000000..f3407a4 --- /dev/null +++ b/src/main/java/nl/sander/jsontoy2/javassist/ClassCreationException.java @@ -0,0 +1,11 @@ +package nl.sander.jsontoy2.javassist; + +public class ClassCreationException extends RuntimeException { + public ClassCreationException(Throwable e) { + super(e); + } + + public ClassCreationException(String message) { + super(message); + } +} diff --git a/src/main/java/nl/sander/jsontoy2/javassist/Javassist.java b/src/main/java/nl/sander/jsontoy2/javassist/Javassist.java new file mode 100644 index 0000000..8f85aa1 --- /dev/null +++ b/src/main/java/nl/sander/jsontoy2/javassist/Javassist.java @@ -0,0 +1,183 @@ +package nl.sander.jsontoy2.javassist; + +import javassist.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static java.util.Arrays.asList; + +/** + * Facade for javassist functions + */ +public class Javassist { + private static final String STRING = "java.lang.String"; + private static final String BOOLEAN = "java.lang.Boolean"; + private static final String CHARACTER = "java.lang.Character"; + private static final String BYTE = "java.lang.Byte"; + private static final String DOUBLE = "java.lang.Double"; + private static final String FLOAT = "java.lang.Float"; + private static final String LONG = "java.lang.Long"; + private static final String SHORT = "java.lang.Short"; + private static final String INTEGER = "java.lang.Integer"; + + private final static Set wrappersAndString = new HashSet<>(asList(BOOLEAN, CHARACTER, BYTE, DOUBLE, FLOAT, LONG, SHORT, INTEGER, STRING)); + private final static Set wrappers = new HashSet<>(asList(BOOLEAN, CHARACTER, BYTE, DOUBLE, FLOAT, LONG, SHORT, INTEGER)); + + private static final String COLLECTION = "java.util.Collection"; + private static final String LIST = "java.util.List"; + private static final String SET = "java.util.Set"; + private static final String MAP = "java.util.Map"; + + private final static ClassPool pool = ClassPool.getDefault(); + + public static CtClass getTypeDefinition(Class type) { + try { + return pool.get(type.getName()); + } catch (NotFoundException e) { + throw new ClassCreationException(e); + } + } + + @SuppressWarnings("unused") + public static CtClass getTypeDefinition(String type) { + try { + return pool.get(type); + } catch (NotFoundException e) { + throw new ClassCreationException(e); + } + } + + public static CtClass createClass(String className, CtClass baseClass) { + return pool.makeClass(className, baseClass); + } + + public static boolean isPrimitiveOrWrapperOrString(CtClass beanClass) { + return beanClass.isPrimitive() || wrappersAndString.contains(beanClass.getName()); + } + + public static boolean isBasicType(CtClass beanClass) { + return isPrimitiveOrWrapperOrString(beanClass) || isList(beanClass); + } + + public static boolean isList(CtClass type) { + try { + List interfaces = new ArrayList<>(asList(type.getInterfaces())); + interfaces.add(type); + for (CtClass interfaze : interfaces) { + if (interfaze.getName().equals(COLLECTION) || interfaze.getName().equals(LIST) || interfaze.getName().equals(SET)) { + return true; + } + } + return false; + } catch (NotFoundException e) { + throw new IllegalStateException(e); + } + } + + public static boolean isCollection(CtClass type) { + try { + List interfaces = new ArrayList<>(asList(type.getInterfaces())); + interfaces.add(type); + for (CtClass interfaze : interfaces) { + if (interfaze.getName().equals(COLLECTION) || interfaze.getName().equals(LIST) || interfaze.getName().equals(SET) || interfaze.getName().equals(MAP)) { + return true; + } + } + return false; + } catch (NotFoundException e) { + throw new IllegalStateException(e); + } + } + + public static boolean isMap(CtClass type) throws NotFoundException { + List interfaces = new ArrayList<>(asList(type.getInterfaces())); + interfaces.add(type); + for (CtClass interfaze : interfaces) { + if (interfaze.getName().equals(MAP)) { + return true; + } + } + return false; + } + + /* + * Retrieves getter methods from a class + */ + public static List getGetters(CtClass type) { + List methods = new ArrayList<>(); + List fields = getAllFields(type); + for (CtField field : fields) { + try { + CtMethod method = type.getMethod(getGetterMethod(field), getDescription(field)); + if (Modifier.isPublic(method.getModifiers())) { + methods.add(method); + } + } catch (NotFoundException n) { + // ignore + } + } + return methods; + } + + private static String getGetterMethod(CtField field) { + return "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1); + } + + private static List getAllFields(CtClass beanClass) { + try { + List allfields = new ArrayList<>(asList(beanClass.getDeclaredFields())); + if (beanClass.getSuperclass() != null) { + return getAllFields(beanClass.getSuperclass(), allfields); + } + return allfields; + } catch (NotFoundException e) { + throw new ClassCreationException(e); + } + } + + private static List getAllFields(CtClass beanClass, List allfields) { + allfields.addAll(asList(beanClass.getDeclaredFields())); + + return allfields; + } + + private static String getDescription(CtField field) throws NotFoundException { + if (field.getType().isArray()) { + return "()[" + innerClassName(field.getType().getName()) + ";"; + } else if (!field.getType().isPrimitive()) { + return "()" + innerClassName(field.getType().getName()) + ";"; + } else { + return "()" + asPrimitive(field.getType().getName()); + } + } + + private static String innerClassName(String name) { + return "L" + name.replaceAll("\\.", "/").replaceAll("\\[]", ""); + } + + private static String asPrimitive(String name) { + switch (name) { + case "int": + return "I"; + case "byte": + return "B"; + case "float": + return "F"; + case "long": + return "J"; + case "boolean": + return "Z"; + case "char": + return "C"; + case "double": + return "D"; + case "short": + return "S"; + } + return ""; + } +} + diff --git a/src/main/java/nl/sander/jsontoy2/readers/AbstractDatesReader.java b/src/main/java/nl/sander/jsontoy2/readers/AbstractDatesReader.java index da1c3f9..f3c2043 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/AbstractDatesReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/AbstractDatesReader.java @@ -1,5 +1,7 @@ package nl.sander.jsontoy2.readers; +import nl.sander.jsontoy2.JsonValueReader; + import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -7,7 +9,7 @@ import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; import java.util.function.Supplier; -public abstract class AbstractDatesReader { +public abstract class AbstractDatesReader extends JsonValueReader { private static final ZoneId zone = ZoneId.systemDefault(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/BooleanReader.java b/src/main/java/nl/sander/jsontoy2/readers/BooleanReader.java index 74033a9..9b526cb 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/BooleanReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/BooleanReader.java @@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers; import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; -public class BooleanReader implements JsonValueReader { +public class BooleanReader extends JsonValueReader { @Override public Boolean read(Parser parser) { return parser.parseBoolean(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/ByteReader.java b/src/main/java/nl/sander/jsontoy2/readers/ByteReader.java index 56804ee..7a0f2c7 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/ByteReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/ByteReader.java @@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers; import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; -public class ByteReader implements JsonValueReader { +public class ByteReader extends JsonValueReader { @Override public Byte read(Parser parser) { return parser.parseByte(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/CharReader.java b/src/main/java/nl/sander/jsontoy2/readers/CharReader.java index cd79650..3408341 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/CharReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/CharReader.java @@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers; import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; -public class CharReader implements JsonValueReader { +public class CharReader extends JsonValueReader { @Override public Character read(Parser parser) { return parser.parseCharacter(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/DateReader.java b/src/main/java/nl/sander/jsontoy2/readers/DateReader.java index 99dbc11..acf6328 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/DateReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/DateReader.java @@ -1,12 +1,11 @@ package nl.sander.jsontoy2.readers; -import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; import java.time.ZonedDateTime; import java.util.Date; -public class DateReader extends AbstractDatesReader implements JsonValueReader { +public class DateReader extends AbstractDatesReader { @Override public Date read(Parser parser) { diff --git a/src/main/java/nl/sander/jsontoy2/readers/DoubleReader.java b/src/main/java/nl/sander/jsontoy2/readers/DoubleReader.java index 148dfde..a6962b2 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/DoubleReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/DoubleReader.java @@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers; import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; -public class DoubleReader implements JsonValueReader { +public class DoubleReader extends JsonValueReader { @Override public Double read(Parser parser) { return parser.parseDouble(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/FloatReader.java b/src/main/java/nl/sander/jsontoy2/readers/FloatReader.java index eec7bcb..611b365 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/FloatReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/FloatReader.java @@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers; import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; -public class FloatReader implements JsonValueReader { +public class FloatReader extends JsonValueReader { @Override public Float read(Parser parser) { return parser.parseFloat(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/IntegerReader.java b/src/main/java/nl/sander/jsontoy2/readers/IntegerReader.java index 9072abf..631d45a 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/IntegerReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/IntegerReader.java @@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers; import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; -public class IntegerReader implements JsonValueReader { +public class IntegerReader extends JsonValueReader { @Override public Integer read(Parser parser) { return parser.parseInteger(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/ListReader.java b/src/main/java/nl/sander/jsontoy2/readers/ListReader.java index 7a51106..7dc5cbf 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/ListReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/ListReader.java @@ -6,7 +6,7 @@ import nl.sander.jsontoy2.Parser; import java.util.List; @SuppressWarnings("rawtypes") -public class ListReader implements JsonValueReader { +public class ListReader extends JsonValueReader { @Override public List read(Parser parser) { return parser.parseArray(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/LocalDateTimeReader.java b/src/main/java/nl/sander/jsontoy2/readers/LocalDateTimeReader.java index e8f6b98..35db186 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/LocalDateTimeReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/LocalDateTimeReader.java @@ -1,12 +1,11 @@ package nl.sander.jsontoy2.readers; -import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; import java.time.LocalDateTime; import java.time.ZonedDateTime; -public class LocalDateTimeReader extends AbstractDatesReader implements JsonValueReader { +public class LocalDateTimeReader extends AbstractDatesReader { @Override public LocalDateTime read(Parser parser) { diff --git a/src/main/java/nl/sander/jsontoy2/readers/LongReader.java b/src/main/java/nl/sander/jsontoy2/readers/LongReader.java index 37bac56..63dae5c 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/LongReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/LongReader.java @@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers; import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; -public class LongReader implements JsonValueReader { +public class LongReader extends JsonValueReader { @Override public Long read(Parser parser) { return parser.parseLong(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/MapReader.java b/src/main/java/nl/sander/jsontoy2/readers/MapReader.java index 2e42722..b691a38 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/MapReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/MapReader.java @@ -6,7 +6,7 @@ import nl.sander.jsontoy2.Parser; import java.util.Map; @SuppressWarnings("rawtypes") -public class MapReader implements JsonValueReader { +public class MapReader extends JsonValueReader { @Override public Map read(Parser parser) { return parser.parseObject(); diff --git a/src/main/java/nl/sander/jsontoy2/readers/ShortReader.java b/src/main/java/nl/sander/jsontoy2/readers/ShortReader.java index c272956..dd01f8d 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/ShortReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/ShortReader.java @@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers; import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; -public class ShortReader implements JsonValueReader { +public class ShortReader extends JsonValueReader { @Override public Short read(Parser parser) { diff --git a/src/main/java/nl/sander/jsontoy2/readers/StringReader.java b/src/main/java/nl/sander/jsontoy2/readers/StringReader.java index d1cdfe1..f86b372 100644 --- a/src/main/java/nl/sander/jsontoy2/readers/StringReader.java +++ b/src/main/java/nl/sander/jsontoy2/readers/StringReader.java @@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers; import nl.sander.jsontoy2.JsonValueReader; import nl.sander.jsontoy2.Parser; -public class StringReader implements JsonValueReader { +public class StringReader extends JsonValueReader { @Override public String read(Parser parser) { diff --git a/src/test/java/nl/sander/jsontoy2/BeanWithBoolean.java b/src/test/java/nl/sander/jsontoy2/BeanWithBoolean.java new file mode 100644 index 0000000..3a6a339 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/BeanWithBoolean.java @@ -0,0 +1,13 @@ +package nl.sander.jsontoy2; + +import nl.sander.jsontoy2.beans.BooleanBean; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BeanWithBoolean { + @Test + public void testTrue() { + assertEquals(new BooleanBean(true, true), JsonReader.read(BooleanBean.class, "{\"value\": true, \"value2\": true}")); + } +} diff --git a/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java b/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java new file mode 100644 index 0000000..8df9b8e --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java @@ -0,0 +1,147 @@ +package nl.sander.jsontoy2; + +import nl.sander.jsontoy2.beans.*; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class WrapperObjectTests { + + @Test + public void testNested() { + SimpleBean bean = JsonReader.read(SimpleBean.class, "{\"data1\": \"value1\",\"data2\": \"value2\"}"); + assertEquals(SimpleBean.class, bean.getClass()); + + assertEquals("value1", bean.getData1()); + assertEquals("value2", bean.getData2()); + } + + @Test + public void testBoolean() { +// BooleanBean trueBean = JsonReader.read(BooleanBean.class, "{\"value\": true}"); +// assertTrue(trueBean.isValue()); + // second call to read, must not recreate class definition, would not compile + // so this test implicitly tests caching function too + BooleanBean falseBean = JsonReader.read(BooleanBean.class, "{\"value2\": false}"); + assertFalse(falseBean.isValue()); + } + + @Test + public void testString() { + StringBean stringBean = JsonReader.read(StringBean.class, "{\"value\": \"haha\"}"); + assertEquals("haha", stringBean.getValue()); + } + + @Test + public void testInteger() { + IntegerBean integerBean = JsonReader.read(IntegerBean.class, "{\"value\": 1,\"value2\": 2}"); + assertEquals(1, integerBean.getValue()); + assertEquals(Integer.valueOf(2), integerBean.getValue2()); + } + + @Test + public void testLong() { + LongBean longBean = JsonReader.read(LongBean.class, "{\"value\": 100000000000,\"value2\": 100000000000}"); + assertEquals(100000000000L, longBean.getValue()); + assertEquals(Long.valueOf(100000000000L), longBean.getValue2()); + } + + @Test + public void testFloat() { + FloatBean floatBean = JsonReader.read(FloatBean.class, "{\"value\": 1.0,\"value2\": 100000000000}"); + assertEquals(1.0F, floatBean.getValue(), 0.1F); + assertEquals(100000000000.0F, floatBean.getValue2(), 0.1F); + } + + @Test + public void testDouble() { + DoubleBean doubleBean = JsonReader.read(DoubleBean.class, "{\"value\": 1.0,\"value2\": 100000000000}"); + assertEquals(1.0D, doubleBean.getValue(), 0.1D); + assertEquals(100000000000.0D, doubleBean.getValue2(), 0.1D); + } + + @Test + public void testShort() { + ShortBean shortBean = JsonReader.read(ShortBean.class, "{\"value\": 1,\"value2\": -11}"); + assertEquals(1, shortBean.getValue()); + assertEquals((short) -11, shortBean.getValue2().shortValue()); + } + + @Test + public void testByte() { + ByteBean byteBean = JsonReader.read(ByteBean.class, "{\"value\": 1,\"value2\": -11}"); + assertEquals(1, byteBean.getValue()); + assertEquals((byte) -11, byteBean.getValue2().shortValue()); + } + + @Test + public void testStringList() { + StringListBean listBean = JsonReader.read(StringListBean.class, "{\"value\": [\"a\",\"b\"]}"); + assertEquals(Arrays.asList("a", "b"), listBean.getValue()); + } + + @Test + public void testStringSet() { + StringSetBean listBean = JsonReader.read(StringSetBean.class, "{\"value\": [\"a\",\"b\"]}"); + assertEquals(new HashSet<>(Arrays.asList("a", "b")), listBean.getValue()); + } + +// @Test +// public void testIntegerList() { +// IntegerListBean listBean = JsonReader.read(IntegerListBean.class, "{\"value\": [1,22]}"); +// assertEquals(Arrays.asList(1, 22), listBean.getValue()); +// } +// +// @Test +// public void testCharacterList() { +// CharacterListBean listBean = JsonReader.read(CharacterListBean.class, "{\"value\": [\"a\", \"[\", \"^\"]}"); +// assertEquals(Arrays.asList('a', '[', '^'), listBean.getValue()); +// } + +// @Test +// public void testShortList() { +// ShortListBean listBean = JsonReader.read(ShortListBean.class, "{\"value\": [-1,0,1]}"); +// assertEquals(Arrays.asList((short) -1, (short) 0, (short) 1), listBean.getValue()); +// } + + @Test + public void testBooleanList() { + BooleanListBean listBean = JsonReader.read(BooleanListBean.class, "{\"value\": [true,false]}"); + assertEquals(Arrays.asList(true, false), listBean.getValue()); + } + +// @Test +// public void testFloatList() { +// FloatListBean listBean = JsonReader.read(FloatListBean.class, "{\"value\": [-100.156,78.0]}"); +// assertEquals(Arrays.asList(-100.156F, 78.0F), listBean.getValue()); +// } +// +// @Test +// public void testByteList() { +// ByteListBean listBean = JsonReader.read(ByteListBean.class, "{\"value\": [-100,78]}"); +// assertEquals(Arrays.asList((byte) -100, (byte) 78), listBean.getValue()); +// } +// +// @Test +// public void testDoubleList() { +// DoubleListBean listBean = JsonReader.read(DoubleListBean.class, "{\"value\": [-100.156,78.0]}"); +// assertEquals(Arrays.asList(-100.156D, 78.0D), listBean.getValue()); +// } + + @Test + public void testNestedBean() { + NestedBean nestedBean = JsonReader.read(NestedBean.class, "{\"value\": {\"value\": \"nested\"}}"); + assertEquals(new NestedBean(new InnerBean("nested")), nestedBean); + } + + @Test + public void testStringMap() { + StringMapBean actual = JsonReader.read(StringMapBean.class, "{\"map\": {\"a:\": \"b\", \"c:\" : \"d\" }}"); + StringMapBean expected = new StringMapBean("a:", "b", "c:", "d"); + assertEquals(expected, actual); + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/BooleanBean.java b/src/test/java/nl/sander/jsontoy2/beans/BooleanBean.java new file mode 100644 index 0000000..3dd43b4 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/BooleanBean.java @@ -0,0 +1,48 @@ +package nl.sander.jsontoy2.beans; + +import java.util.Objects; + +public class BooleanBean { + private boolean value; + private Boolean value2; + + public BooleanBean() { + } + + public BooleanBean(boolean value, Boolean value2) { + this.value = value; + this.value2 = value2; + } + + public boolean isValue() { + return value; + } + + public void setValue(boolean value) { + this.value = value; + } + + public Boolean getValue2() { + return value2; + } + + public void setValue2(Boolean value2) { + this.value2 = value2; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BooleanBean that = (BooleanBean) o; + return value == that.value && + value2.equals(that.value2); + } + + @Override + public int hashCode() { + return Objects.hash(value, value2); + } + + +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/BooleanListBean.java b/src/test/java/nl/sander/jsontoy2/beans/BooleanListBean.java new file mode 100644 index 0000000..877106d --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/BooleanListBean.java @@ -0,0 +1,22 @@ +package nl.sander.jsontoy2.beans; + +import java.util.List; + +public class BooleanListBean { + private List value; + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } + + @Override + public String toString() { + return "BooleanListBean{" + + "value=" + value + + '}'; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/ByteBean.java b/src/test/java/nl/sander/jsontoy2/beans/ByteBean.java new file mode 100644 index 0000000..6b64fc9 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/ByteBean.java @@ -0,0 +1,25 @@ +package nl.sander.jsontoy2.beans; + +public class ByteBean { + private byte value; + private Byte value2; + + public ByteBean() { + } + + public byte getValue() { + return value; + } + + public void setValue(byte value) { + this.value = value; + } + + public Byte getValue2() { + return value2; + } + + public void setValue2(Byte value2) { + this.value2 = value2; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/ByteListBean.java b/src/test/java/nl/sander/jsontoy2/beans/ByteListBean.java new file mode 100644 index 0000000..5457140 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/ByteListBean.java @@ -0,0 +1,15 @@ +package nl.sander.jsontoy2.beans; + +import java.util.List; + +public class ByteListBean { + private List value; + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/CharacterListBean.java b/src/test/java/nl/sander/jsontoy2/beans/CharacterListBean.java new file mode 100644 index 0000000..b8fd9a2 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/CharacterListBean.java @@ -0,0 +1,15 @@ +package nl.sander.jsontoy2.beans; + +import java.util.List; + +public class CharacterListBean { + private List value; + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/DoubleBean.java b/src/test/java/nl/sander/jsontoy2/beans/DoubleBean.java new file mode 100644 index 0000000..a966641 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/DoubleBean.java @@ -0,0 +1,25 @@ +package nl.sander.jsontoy2.beans; + +public class DoubleBean { + private double value; + private Double value2; + + public DoubleBean() { + } + + public double getValue() { + return value; + } + + public void setValue(double value) { + this.value = value; + } + + public Double getValue2() { + return value2; + } + + public void setValue2(Double value2) { + this.value2 = value2; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/DoubleListBean.java b/src/test/java/nl/sander/jsontoy2/beans/DoubleListBean.java new file mode 100644 index 0000000..285449d --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/DoubleListBean.java @@ -0,0 +1,25 @@ +package nl.sander.jsontoy2.beans; + +import nl.sander.jsontoy2.JsonReader; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DoubleListBean { + private List value; + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } + + @Test + public void testTrue() { + assertEquals(true, JsonReader.read(Boolean.class, "true")); + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/FloatBean.java b/src/test/java/nl/sander/jsontoy2/beans/FloatBean.java new file mode 100644 index 0000000..4b48a76 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/FloatBean.java @@ -0,0 +1,25 @@ +package nl.sander.jsontoy2.beans; + +public class FloatBean { + private float value; + private Float value2; + + public FloatBean() { + } + + public float getValue() { + return value; + } + + public void setValue(float value) { + this.value = value; + } + + public Float getValue2() { + return value2; + } + + public void setValue2(Float value2) { + this.value2 = value2; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/FloatListBean.java b/src/test/java/nl/sander/jsontoy2/beans/FloatListBean.java new file mode 100644 index 0000000..11a4368 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/FloatListBean.java @@ -0,0 +1,15 @@ +package nl.sander.jsontoy2.beans; + +import java.util.List; + +public class FloatListBean { + private List value; + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/InnerBean.java b/src/test/java/nl/sander/jsontoy2/beans/InnerBean.java new file mode 100644 index 0000000..fda1b5a --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/InnerBean.java @@ -0,0 +1,42 @@ +package nl.sander.jsontoy2.beans; + +import java.util.Objects; + +public class InnerBean { + private String value; + + public InnerBean() { + } + + public InnerBean(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InnerBean innerBean = (InnerBean) o; + return value.equals(innerBean.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "InnerBean{" + + "value='" + value + '\'' + + '}'; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/IntegerBean.java b/src/test/java/nl/sander/jsontoy2/beans/IntegerBean.java new file mode 100644 index 0000000..de63843 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/IntegerBean.java @@ -0,0 +1,25 @@ +package nl.sander.jsontoy2.beans; + +public class IntegerBean { + private int value; + private Integer value2; + + public IntegerBean() { + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + public Integer getValue2() { + return value2; + } + + public void setValue2(Integer value2) { + this.value2 = value2; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/IntegerListBean.java b/src/test/java/nl/sander/jsontoy2/beans/IntegerListBean.java new file mode 100644 index 0000000..17adfa9 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/IntegerListBean.java @@ -0,0 +1,15 @@ +package nl.sander.jsontoy2.beans; + +import java.util.List; + +public class IntegerListBean { + private List value; + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/LongBean.java b/src/test/java/nl/sander/jsontoy2/beans/LongBean.java new file mode 100644 index 0000000..deae52b --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/LongBean.java @@ -0,0 +1,25 @@ +package nl.sander.jsontoy2.beans; + +public class LongBean { + private long value; + private Long value2; + + public LongBean() { + } + + public long getValue() { + return value; + } + + public void setValue(long value) { + this.value = value; + } + + public Long getValue2() { + return value2; + } + + public void setValue2(Long value2) { + this.value2 = value2; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/NestedBean.java b/src/test/java/nl/sander/jsontoy2/beans/NestedBean.java new file mode 100644 index 0000000..b2d3913 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/NestedBean.java @@ -0,0 +1,48 @@ +package nl.sander.jsontoy2.beans; + +import java.util.Objects; + +public class NestedBean { + + private InnerBean value; + private String empty; + + public NestedBean() { + } + + public NestedBean(InnerBean value) { + this.value = value; + } + + public InnerBean getValue() { + return value; + } + + public void setValue(InnerBean value) { + this.value = value; + } + + public void setEmpty(String empty) { + this.empty = empty; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NestedBean that = (NestedBean) o; + return value.equals(that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "NestedBean{" + + "value=" + value + + '}'; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/ShortBean.java b/src/test/java/nl/sander/jsontoy2/beans/ShortBean.java new file mode 100644 index 0000000..64b5e52 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/ShortBean.java @@ -0,0 +1,25 @@ +package nl.sander.jsontoy2.beans; + +public class ShortBean { + private short value; + private Short value2; + + public ShortBean() { + } + + public short getValue() { + return value; + } + + public void setValue(short value) { + this.value = value; + } + + public Short getValue2() { + return value2; + } + + public void setValue2(Short value2) { + this.value2 = value2; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/ShortListBean.java b/src/test/java/nl/sander/jsontoy2/beans/ShortListBean.java new file mode 100644 index 0000000..43f6b1f --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/ShortListBean.java @@ -0,0 +1,15 @@ +package nl.sander.jsontoy2.beans; + +import java.util.List; + +public class ShortListBean { + private List value; + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/SimpleBean.java b/src/test/java/nl/sander/jsontoy2/beans/SimpleBean.java new file mode 100644 index 0000000..e871756 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/SimpleBean.java @@ -0,0 +1,22 @@ +package nl.sander.jsontoy2.beans; + +public class SimpleBean { + String data1; + String data2; + + public String getData1() { + return data1; + } + + public void setData1(String data1) { + this.data1 = data1; + } + + public String getData2() { + return data2; + } + + public void setData2(String data2) { + this.data2 = data2; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/StringBean.java b/src/test/java/nl/sander/jsontoy2/beans/StringBean.java new file mode 100644 index 0000000..5008e34 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/StringBean.java @@ -0,0 +1,13 @@ +package nl.sander.jsontoy2.beans; + +public class StringBean { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/StringListBean.java b/src/test/java/nl/sander/jsontoy2/beans/StringListBean.java new file mode 100644 index 0000000..3b05cbe --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/StringListBean.java @@ -0,0 +1,15 @@ +package nl.sander.jsontoy2.beans; + +import java.util.List; + +public class StringListBean { + private List value; + + public List getValue() { + return value; + } + + public void setValue(List value) { + this.value = value; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/StringMapBean.java b/src/test/java/nl/sander/jsontoy2/beans/StringMapBean.java new file mode 100644 index 0000000..c57098d --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/StringMapBean.java @@ -0,0 +1,46 @@ +package nl.sander.jsontoy2.beans; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class StringMapBean { + + private Map map = new HashMap<>(); + + public StringMapBean() { + this.map = new HashMap<>(); + } + + public StringMapBean(String... keysAndValues) { + if (keysAndValues.length % 2 == 1) { + throw new IllegalArgumentException("uneven number of arguments is not allowed here"); + } + for (int i = 0; i < keysAndValues.length; ) { + String key = keysAndValues[i++]; + String value = keysAndValues[i++]; + map.put(key, value); + } + } + + public void setMap(Map map) { + this.map = map; + } + + public Map getMap() { + return map; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StringMapBean that = (StringMapBean) o; + return map.equals(that.map); + } + + @Override + public int hashCode() { + return Objects.hash(map); + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/StringSetBean.java b/src/test/java/nl/sander/jsontoy2/beans/StringSetBean.java new file mode 100644 index 0000000..de6eb4f --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/StringSetBean.java @@ -0,0 +1,15 @@ +package nl.sander.jsontoy2.beans; + +import java.util.Set; + +public class StringSetBean { + private Set value; + + public Set getValue() { + return value; + } + + public void setValue(Set value) { + this.value = value; + } +}