From f4d9d0c8c5449821d797078b40a3daa0496241cc Mon Sep 17 00:00:00 2001 From: Shautvast Date: Tue, 20 Jun 2023 18:13:30 +0200 Subject: [PATCH] changed everything --- README.md | 12 +- .../jsontoy/deserialize/Deserializer.java | 16 - .../jsontoy/serialize/JSONSerializer.java | 21 -- .../nl/jssl/jsontoy/serialize/Serializer.java | 43 --- .../SerializerCreationException.java | 10 - .../serialize/SynthSerializerFactory.java | 348 ------------------ .../sanderhautvast/json/ser/BaseMapper.java | 7 + .../json/ser/ByteClassLoader.java | 23 ++ .../nl/sanderhautvast/json/ser/JsonError.java | 7 + .../nl/sanderhautvast/json/ser/Mapper.java | 302 +++++++++++++++ .../json/ser/MapperFactory.java | 148 ++++++++ .../jsontoy/serialize/StringPropertyTest.java | 51 --- .../serialize/collections/ArrayTest.java | 34 -- .../serialize/collections/ListTest.java | 41 --- .../serialize/collections/MapTest.java | 47 --- .../serialize/collections/SetTest.java | 43 --- .../jssl/jsontoy/serialize/nested/Bean1.java | 48 --- .../jssl/jsontoy/serialize/nested/Bean2.java | 13 - .../jsontoy/serialize/nested/Bean2Child.java | 14 - .../serialize/nested/NestedBeanTest.java | 37 -- .../primitives/BooleanPropertyTest.java | 39 -- .../primitives/BytePropertyTest.java | 38 -- .../primitives/CharPropertyTest.java | 38 -- .../primitives/DoublePropertyTest.java | 38 -- .../primitives/FloatPropertyTest.java | 38 -- .../serialize/primitives/IntPropertyTest.java | 39 -- .../primitives/LongPropertyTest.java | 38 -- .../primitives/NullPropertyTest.java | 33 -- .../primitives/ShortPropertyTest.java | 38 -- .../json/ser/ByteClassLoaderTest.java | 14 + .../json/ser/StringPropertyTest.java | 37 ++ .../json/ser/collections/ArrayTest.java | 28 ++ .../json/ser/collections/ListTest.java | 36 ++ .../json/ser/collections/MapTest.java | 47 +++ .../json/ser/collections/SetTest.java | 38 ++ .../json/ser/collections/StringTests.java | 44 +++ .../sanderhautvast/json/ser/nested/Bean1.java | 10 + .../sanderhautvast/json/ser/nested/Bean2.java | 9 + .../json/ser/nested/NestedBeanTest.java | 37 ++ .../performance/JacksonComparisonTest.java | 25 +- .../ser/primitives/BooleanPropertyTest.java | 32 ++ .../json/ser/primitives/BytePropertyTest.java | 31 ++ .../json/ser/primitives/CharPropertyTest.java | 31 ++ .../ser/primitives/DoublePropertyTest.java | 31 ++ .../ser/primitives/FloatPropertyTest.java | 31 ++ .../json/ser/primitives/IntPropertyTest.java | 32 ++ .../json/ser/primitives/LongPropertyTest.java | 32 ++ .../json/ser/primitives/NullPropertyTest.java | 21 ++ .../ser/primitives/ShortPropertyTest.java | 31 ++ 49 files changed, 1078 insertions(+), 1123 deletions(-) delete mode 100644 src/main/java/nl/jssl/jsontoy/deserialize/Deserializer.java delete mode 100644 src/main/java/nl/jssl/jsontoy/serialize/JSONSerializer.java delete mode 100644 src/main/java/nl/jssl/jsontoy/serialize/Serializer.java delete mode 100644 src/main/java/nl/jssl/jsontoy/serialize/SerializerCreationException.java delete mode 100644 src/main/java/nl/jssl/jsontoy/serialize/SynthSerializerFactory.java create mode 100644 src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java create mode 100644 src/main/java/nl/sanderhautvast/json/ser/ByteClassLoader.java create mode 100644 src/main/java/nl/sanderhautvast/json/ser/JsonError.java create mode 100644 src/main/java/nl/sanderhautvast/json/ser/Mapper.java create mode 100644 src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/StringPropertyTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/collections/ArrayTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/collections/ListTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/collections/MapTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/collections/SetTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/nested/Bean1.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/nested/Bean2.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/nested/Bean2Child.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/nested/NestedBeanTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/primitives/BooleanPropertyTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/primitives/BytePropertyTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/primitives/CharPropertyTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/primitives/DoublePropertyTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/primitives/FloatPropertyTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/primitives/IntPropertyTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/primitives/LongPropertyTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/primitives/NullPropertyTest.java delete mode 100644 src/test/java/nl/jssl/jsontoy/serialize/primitives/ShortPropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/ByteClassLoaderTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/collections/ArrayTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/collections/StringTests.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/nested/Bean2.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/nested/NestedBeanTest.java rename src/test/java/nl/{jssl/jsontoy/serialize => sanderhautvast/json/ser}/performance/JacksonComparisonTest.java (82%) create mode 100644 src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/primitives/CharPropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/primitives/DoublePropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/primitives/IntPropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/primitives/NullPropertyTest.java create mode 100644 src/test/java/nl/sanderhautvast/json/ser/primitives/ShortPropertyTest.java diff --git a/README.md b/README.md index 9675f6b..617c856 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -# JsonToy +# Json a JSON serializer based on bytecode manipulation -* creates a Json serializer for a java type using javassist -* deserializing not yet implemented -* see the unit tests to see how it works +* uses ASM for creating object mappers +* deserializing not (yet?) implemented +* write to String: Mapper.json(...) -* as of java9 it needs `--add-opens java.base/java.lang=ALL-UNNAMED` as java commandline option. \ No newline at end of file +* ~~as of java9 it needs `--add-opens java.base/java.lang=ALL-UNNAMED` as java commandline option.~~ + +* prerequisite: at least jdk9 \ No newline at end of file diff --git a/src/main/java/nl/jssl/jsontoy/deserialize/Deserializer.java b/src/main/java/nl/jssl/jsontoy/deserialize/Deserializer.java deleted file mode 100644 index 0619f7f..0000000 --- a/src/main/java/nl/jssl/jsontoy/deserialize/Deserializer.java +++ /dev/null @@ -1,16 +0,0 @@ -package nl.jssl.jsontoy.deserialize; - -public class Deserializer { - - private int position; - private final String jsonString; - - private Deserializer( String jsonString,int position) { - this.position = position; - this.jsonString = jsonString; - } - - - - -} diff --git a/src/main/java/nl/jssl/jsontoy/serialize/JSONSerializer.java b/src/main/java/nl/jssl/jsontoy/serialize/JSONSerializer.java deleted file mode 100644 index fd6ee54..0000000 --- a/src/main/java/nl/jssl/jsontoy/serialize/JSONSerializer.java +++ /dev/null @@ -1,21 +0,0 @@ -package nl.jssl.jsontoy.serialize; - -import java.util.Formatter; - -public abstract class JSONSerializer { - protected abstract String handle(T object); - - protected Formatter formatter = new Formatter(); - - public String toJSONString(T object) { - if (object == null) { - return ""; - } else if (object instanceof Number || object instanceof Boolean) { - return "" + object; - } else if (object instanceof CharSequence || object instanceof Character) { - return "\"" + object + "\""; - } else { - return handle(object); - } - } -} diff --git a/src/main/java/nl/jssl/jsontoy/serialize/Serializer.java b/src/main/java/nl/jssl/jsontoy/serialize/Serializer.java deleted file mode 100644 index 22f6b76..0000000 --- a/src/main/java/nl/jssl/jsontoy/serialize/Serializer.java +++ /dev/null @@ -1,43 +0,0 @@ -package nl.jssl.jsontoy.serialize; - -public class Serializer { - private static SynthSerializerFactory instance = new SynthSerializerFactory(); - - public static String toJSONString(boolean b) { - return Boolean.toString(b); - } - - public static String toJSONString(short s) { - return Short.toString(s); - } - - public static String toJSONString(int i) { - return Integer.toString(i); - } - - public static String toJSONString(float f) { - return Float.toString(f); - } - - public static String toJSONString(double d) { - return Double.toString(d); - } - - public static String toJSONString(long l) { - return Long.toString(l); - } - - public static String toJSONString(char c) { - return "\"" + c + "\""; - } - - @SuppressWarnings("unchecked") - public static String toJSONString(T o) { - if (o == null) { - return "null"; - } - return instance.createSerializer((Class) o.getClass()).toJSONString(o); - } - - -} diff --git a/src/main/java/nl/jssl/jsontoy/serialize/SerializerCreationException.java b/src/main/java/nl/jssl/jsontoy/serialize/SerializerCreationException.java deleted file mode 100644 index 3d9ec86..0000000 --- a/src/main/java/nl/jssl/jsontoy/serialize/SerializerCreationException.java +++ /dev/null @@ -1,10 +0,0 @@ -package nl.jssl.jsontoy.serialize; - -@SuppressWarnings("serial") -public class SerializerCreationException extends RuntimeException { - - public SerializerCreationException(Throwable t) { - super(t); - } - -} diff --git a/src/main/java/nl/jssl/jsontoy/serialize/SynthSerializerFactory.java b/src/main/java/nl/jssl/jsontoy/serialize/SynthSerializerFactory.java deleted file mode 100644 index 87c797a..0000000 --- a/src/main/java/nl/jssl/jsontoy/serialize/SynthSerializerFactory.java +++ /dev/null @@ -1,348 +0,0 @@ -package nl.jssl.jsontoy.serialize; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javassist.CannotCompileException; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.CtField; -import javassist.CtMethod; -import javassist.CtNewMethod; -import javassist.Modifier; -import javassist.NotFoundException; - -class SynthSerializerFactory { - 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<>(Arrays.asList(BOOLEAN, CHARACTER, BYTE, DOUBLE, FLOAT, LONG, SHORT, INTEGER, - STRING)); - - 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 static final Map, JSONSerializer> serializers = new HashMap<>(); - private static final String ROOT_PACKAGE = "serializer."; - - private final ClassPool pool = ClassPool.getDefault(); - private CtClass serializerBase; - - - public SynthSerializerFactory() { - init(); - } - - void init() { - try { - serializerBase = pool.get(JSONSerializer.class.getName()); - } catch (NotFoundException e) { - throw new SerializerCreationException(e); - } - } - - @SuppressWarnings("unchecked") - JSONSerializer createSerializer(Class beanjavaClass) { - JSONSerializer serializer = (JSONSerializer) serializers.get(beanjavaClass); - if (serializer != null) { - return serializer; - } - try { - return tryCreateSerializer(beanjavaClass); - } catch (NotFoundException | CannotCompileException | InstantiationException | IllegalAccessException | - InvocationTargetException | NoSuchMethodException e) { - throw new SerializerCreationException(e); - } - } - - private JSONSerializer tryCreateSerializer(Class javaClass) throws NotFoundException, CannotCompileException, InstantiationException, - IllegalAccessException, InvocationTargetException, NoSuchMethodException { - CtClass beanClass = pool.get(javaClass.getName()); - CtClass serializerClass = pool.makeClass(createSerializerName(beanClass), serializerBase); - - addToJsonStringMethod(beanClass, serializerClass); - - JSONSerializer jsonSerializer = createSerializerInstance(serializerClass); - - serializers.put(javaClass, jsonSerializer); - return jsonSerializer; - } - - /* - * create method source, compile it and add it to the class under construction - */ - private void addToJsonStringMethod(CtClass beanClass, CtClass serializerClass) throws NotFoundException, CannotCompileException { - serializerClass.addMethod(CtNewMethod.make(createToJSONStringMethodSource(beanClass), serializerClass)); - } - - /* - * Creates the source, handling the for JSON different types of classes - */ - private String createToJSONStringMethodSource(CtClass beanClass) throws NotFoundException { - String source = "public String handle(Object object){\n"; - if (beanClass.isArray()) { - source += "\tObject[] array=(Object[])object;\n"; - source += handleArray(); - } else if (isCollection(beanClass)) { - source += "\tObject[] array=((java.util.Collection)object).toArray();\n"; - source += handleArray(); - } else if (isMap(beanClass)) { - source += handleMap(); - } else if (!isPrimitiveOrWrapperOrString(beanClass)) { - List getters = getGetters(beanClass); - if (shouldAddGetterCallers(getters)) { - source = addGetterCallers(beanClass, source, getters); - } - } else { - source += "\treturn \"\";}"; - } - return source; - } - - /* - * Any Collection is converted to an array, after which code is generated to handle the single elements. - * - * A subserializer is created for every single element, but most of the time it will be the same cached instance. - * - * The generated code fills a StringBuilder. The values are generated by the subserializers - */ - private String handleArray() { - String source = "\tStringBuilder result=new StringBuilder(\"[\");\n"; - source += "\tfor (int i=0; i getters) { - int index = 0; - source += "\treturn "; - source += "\"{"; - for (CtMethod getter : getters) { - source = addPair(beanClass, source, getter); - if (index++ < getters.size() - 1) { - source += ","; - } - } - source += "}\";\n}"; - return source; - } - - @SuppressWarnings("unchecked") - private JSONSerializer createSerializerInstance(CtClass serializerClass) throws InstantiationException, IllegalAccessException, - CannotCompileException, NoSuchMethodException, InvocationTargetException { - return (JSONSerializer) serializerClass.toClass().getConstructor().newInstance(); - } - - /* - * 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 - */ - public String createSerializerName(CtClass beanClass) { - return createSerializerName(beanClass.getName()); - } - - public String createSerializerName(String name) { - return ROOT_PACKAGE + name.replaceAll("\\[]", "Array") + "Serializer"; - } - - private boolean isCollection(CtClass beanClass) throws NotFoundException { - List interfaces = new ArrayList<>(Arrays.asList(beanClass.getInterfaces())); - interfaces.add(beanClass); - for (CtClass interfaze : interfaces) { - if (interfaze.getName().equals(COLLECTION) || interfaze.getName().equals(LIST) || interfaze.getName().equals(SET)) { - return true; - } - } - return false; - } - - private boolean isMap(CtClass beanClass) throws NotFoundException { - List interfaces = new ArrayList<>(Arrays.asList(beanClass.getInterfaces())); - interfaces.add(beanClass); - for (CtClass interfaze : interfaces) { - if (interfaze.getName().equals(MAP)) { - return true; - } - } - return false; - } - - /* - * The JSON vernacular for key:value is pair... - */ - private String addPair(CtClass classToSerialize, String source, CtMethod getter) { - source += jsonKey(getter); - source += ": "; // what is the rule when it comes to spaces in json? - source += jsonValue(classToSerialize, getter); - return source; - } - - /* - * derive property key from getter - */ - private String jsonKey(CtMethod getter) { - return "\\\"" + toFieldName(getter.getName()) + "\\\""; - } - - private String jsonValue(CtClass classToSerialize, CtMethod getter) { - /* primitives are wrapped so the produced methods adhere to the JSONSerializer interface */ - return createSubSerializerForReturnTypeAndAddInvocationToSource(classToSerialize, getter); - } - - private String createSubSerializerForReturnTypeAndAddInvocationToSource(CtClass classToSerialize, CtMethod getter) { - /* NB there does not seem to be auto(un))boxing nor generic types (or other jdk1.5 stuff) in javassist compileable code */ - String source = "\"+" + Serializer.class.getName() + ".toJSONString("; - - // cast because of lack of generics - source += "(" + cast(regularClassname(classToSerialize.getName())) + "object)." + getter.getName() + "()"; - - source += ")+\""; - return source; - } - - /* - * turns for example 'getValue' into 'value' - */ - private String toFieldName(String name) { - return name.substring(3, 4).toLowerCase() + (name.length() > 4 ? name.substring(4) : ""); - } - - public String regularClassname(String name) { - return name.replaceAll("\\$", "."); - } - - private String cast(String classToSerialize) { - return "(" + classToSerialize + ")"; - } - - /* - * Retrieves getter methods from a class - */ - private List getGetters(CtClass beanClass) { - List methods = new ArrayList<>(); - List fields = getAllFields(beanClass); - for (CtField field : fields) { - try { - CtMethod method = beanClass.getMethod(getGetterMethod(field), getDescription(field)); - if (Modifier.isPublic(method.getModifiers())) { - methods.add(method); - } - } catch (NotFoundException n) { - // ignore - } - } - return methods; - } - - private String getGetterMethod(CtField field) { - return "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1); - } - - private List getAllFields(CtClass beanClass) { - try { - List allfields = new ArrayList<>(Arrays.asList(beanClass.getDeclaredFields())); - if (beanClass.getSuperclass() != null) { - return getAllFields(beanClass.getSuperclass(), allfields); - } - return allfields; - } catch (NotFoundException e) { - throw new SerializerCreationException(e); - } - - } - - private List getAllFields(CtClass beanClass, List allfields) { - allfields.addAll(Arrays.asList(beanClass.getDeclaredFields())); - - return allfields; - } - - /* - * is getter list is not empty then callers should be added - */ - boolean shouldAddGetterCallers(List getters) { - return !getters.isEmpty(); - } - - 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()); - } - } - - 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 ""; - } - - String innerClassName(String name) { - return "L" + name.replaceAll("\\.", "/").replaceAll("\\[]", ""); - } - - static boolean isPrimitiveOrWrapperOrString(CtClass beanClass) { - return beanClass.isPrimitive() || wrappersAndString.contains(beanClass.getName()); - } -} diff --git a/src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java b/src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java new file mode 100644 index 0000000..b383f5a --- /dev/null +++ b/src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java @@ -0,0 +1,7 @@ +package nl.sanderhautvast.json.ser; + +public abstract class BaseMapper { + + protected abstract String json(T value); + +} diff --git a/src/main/java/nl/sanderhautvast/json/ser/ByteClassLoader.java b/src/main/java/nl/sanderhautvast/json/ser/ByteClassLoader.java new file mode 100644 index 0000000..e220a0e --- /dev/null +++ b/src/main/java/nl/sanderhautvast/json/ser/ByteClassLoader.java @@ -0,0 +1,23 @@ +package nl.sanderhautvast.json.ser; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class ByteClassLoader extends ClassLoader { + + private final ConcurrentMap> classes = new ConcurrentHashMap<>(); + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + Class instance = classes.get(name); + if (instance == null) { + throw new ClassNotFoundException(name); + } + return instance; + } + + void addClass(String name, byte[] bytecode) { + Class classDef = defineClass(name, bytecode, 0, bytecode.length); + classes.put(name, classDef); + } +} diff --git a/src/main/java/nl/sanderhautvast/json/ser/JsonError.java b/src/main/java/nl/sanderhautvast/json/ser/JsonError.java new file mode 100644 index 0000000..e5592ed --- /dev/null +++ b/src/main/java/nl/sanderhautvast/json/ser/JsonError.java @@ -0,0 +1,7 @@ +package nl.sanderhautvast.json.ser; + +public class JsonError extends RuntimeException{ + public JsonError(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/nl/sanderhautvast/json/ser/Mapper.java b/src/main/java/nl/sanderhautvast/json/ser/Mapper.java new file mode 100644 index 0000000..fca03e6 --- /dev/null +++ b/src/main/java/nl/sanderhautvast/json/ser/Mapper.java @@ -0,0 +1,302 @@ +package nl.sanderhautvast.json.ser; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + + +public class Mapper { + private static final Map, BaseMapper> mappers = new ConcurrentHashMap<>(); + + private static final ByteClassLoader generatedClassesLoader = new ByteClassLoader(); + + static { + addMapper(String.class, new StringMapper()); + addMapper(Boolean.class, new BooleanMapper()); + addMapper(Integer.class, new IntegerMapper()); + addMapper(Long.class, new LongMapper()); + addMapper(Short.class, new ShortMapper()); + addMapper(Byte.class, new ByteMapper()); + addMapper(Character.class, new CharMapper()); + addMapper(Float.class, new FloatMapper()); + addMapper(Double.class, new DoubleMapper()); + } + + /** + * Add a new (custom) mapper implementation for the specified type + * + * @param type The class to serialize to json + * @param mapper the Mapper implementation + */ + public static void addMapper(Class type, BaseMapper mapper) { + mappers.put(type, mapper); + } + + /** + * returns the json representation of the value as a String + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static String json(Object value) { + if (value == null) { + return "null"; + } + Class type = (Class) value.getClass(); + if (type.isArray()) { + return array((Object[]) value); + } + if (value instanceof Collection) { + return list((Collection) value); + } + if (value instanceof Map) { + return object((Map) value); + } + BaseMapper mapper = mappers.computeIfAbsent(type, key -> createObjectMapper(type)); + return mapper.json(value); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static String array(Object[] array) { + if (array.length == 0) { + return "[]"; + } + Object first = array[0]; + Class elementType = first.getClass(); + BaseMapper mapper = mappers.computeIfAbsent(elementType, key -> createObjectMapper(elementType)); + StringBuilder builder = new StringBuilder("["); + builder.append(mapper.json(first)); + Arrays.stream(array).skip(1) + .forEach(element -> { + builder.append(","); + builder.append(mapper.json(element)); + }); + builder.append("]"); + return builder.toString(); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static String list(Collection list) { + if (list.isEmpty()) { + return "[]"; + } + Object first = list.iterator().next(); + Class elementType = first.getClass(); + BaseMapper mapper = mappers.computeIfAbsent(elementType, key -> createObjectMapper(elementType)); + StringBuilder builder = new StringBuilder("["); + builder.append(mapper.json(first)); + list.stream().skip(1) + .forEach(element -> { + builder.append(","); + builder.append(mapper.json(element)); + }); + builder.append("]"); + return builder.toString(); + } + + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static String object(Map map) { + if (map.isEmpty()) { + return "{}"; + } + Set entries = map.entrySet(); + Map.Entry first = entries.iterator().next(); + Class valueType = first.getValue().getClass(); + + BaseMapper mapper = mappers.computeIfAbsent(valueType, key -> createObjectMapper(valueType)); + StringBuilder builder = new StringBuilder("{"); + builder.append("\"").append(first.getKey()).append("\":").append(mapper.json(first.getValue())); + entries.stream().skip(1) + .forEach(entry -> { + builder.append(",\""); + builder.append(entry.getKey()).append("\":"); + builder.append(mapper.json(entry.getValue())); + }); + builder.append("}"); + return builder.toString(); + } + + public static String json(byte value) { + return Byte.toString(value); + } + + public static String json(boolean value) { + return Boolean.toString(value); + } + + public static String json(short value) { + return Short.toString(value); + } + + public static String json(int value) { + return Integer.toString(value); + } + + public static String json(long value) { + return Long.toString(value); + } + + public static String json(char value) { + return "\"" + escape(value) + "\""; + } + + public static String json(float value) { + return Float.toString(value); + } + + public static String json(double value) { + return Double.toString(value); + } + + @SuppressWarnings("unchecked") + private static BaseMapper createObjectMapper(Class forType) { + try { + ClassReader cr = new ClassReader(forType.getName()); + MapperFactory mapperFactory = new MapperFactory(); + cr.accept(mapperFactory, 0); + ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + + mapperFactory.classNode.accept(classWriter); + byte[] byteArray = classWriter.toByteArray(); + generatedClassesLoader.addClass(mapperFactory.classNode.name, byteArray); + return (BaseMapper) generatedClassesLoader.loadClass(mapperFactory.classNode.name).getConstructor().newInstance(); + } catch (IOException | ClassNotFoundException | NoSuchMethodException | InstantiationException | + IllegalAccessException | InvocationTargetException e) { + throw new JsonError(e); + } + } + + private static String escape(char c) { + return escape(String.valueOf(c)); + } + + static String escape(String value) { + StringBuilder b = new StringBuilder(value); + int i = 0; + while (i < b.length()) { + char c = b.charAt(i); + switch (c) { + case '\t': + b.replace(i,i+1, "\\"); + b.insert(i+1, "t"); + break; + case '\"': + b.replace(i,i+1, "\\"); + b.insert(++i, "\""); + break; + case '/': + b.replace(i,i+1, "\\"); + b.insert(++i, "/"); + break; + case '\r': + b.replace(i,i+1, "\\"); + b.insert(++i, "r"); + break; + case '\n': + b.replace(i,i+1, "\\"); + b.insert(++i, "n"); + break; + case '\b': + b.replace(i,i+1, "\\"); + b.insert(++i, "b"); + break; + case '\f': + b.replace(i,i+1, "\\"); + b.insert(++i, "f"); + break; + case '\\': + b.replace(i,i+1, "\\"); + b.insert(++i, "\\"); + break; + case '\'': + break; + default: + if ((c <= '\u001F') || (c >= '\u007F' && c <= '\u009F') || (c >= '\u2000' && c <= '\u20FF')) { + String ss = Integer.toHexString(c); + b.replace(i,i+1,"\\"); + b.insert(++i, "u"); + for (int k = 0; k < 4 - ss.length(); k++) { + b.insert(++i,'0'); + } + b.insert(++i,ss.toUpperCase()); + } + } + i++; + } + return b.toString(); + } +} + +class BooleanMapper extends BaseMapper { + + @Override + public String json(Boolean b) { + return Boolean.toString(b); + } +} + +class ShortMapper extends BaseMapper { + + @Override + public String json(Short value) { + return Short.toString(value); + } +} + +class StringMapper extends BaseMapper { + @Override + public String json(String value) { + return "\"" + Mapper.escape(value) + "\""; + } +} + +class IntegerMapper extends BaseMapper { + + @Override + public String json(Integer value) { + return Integer.toString(value); + } +} + +class LongMapper extends BaseMapper { + + @Override + public String json(Long value) { + return Long.toString(value); + } +} + +class ByteMapper extends BaseMapper { + + @Override + protected String json(Byte value) { + return Byte.toString(value); + } +} + +class CharMapper extends BaseMapper { + + @Override + protected String json(Character value) { + return "\"" + value + "\""; + } +} + +class FloatMapper extends BaseMapper { + + @Override + protected String json(Float value) { + return Float.toString(value); + } +} + +class DoubleMapper extends BaseMapper { + + @Override + protected String json(Double value) { + return Double.toString(value); + } +} \ No newline at end of file diff --git a/src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java b/src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java new file mode 100644 index 0000000..0b50136 --- /dev/null +++ b/src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java @@ -0,0 +1,148 @@ +package nl.sanderhautvast.json.ser; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.tree.*; + +import java.lang.reflect.Modifier; +import java.util.LinkedList; +import java.util.UUID; + +import static org.objectweb.asm.Opcodes.*; + +public class MapperFactory extends ClassVisitor { + + public static final String MAPPERBASECLASS = javaName(BaseMapper.class.getName()); + + public static final String INIT = ""; + public static final String ZERO_ARGS_VOID = "()V"; + public static final String APPEND = "append"; + public static final String STRINGBUILDER = "java/lang/StringBuilder"; + public static final String MAPPER = "nl/sanderhautvast/json/ser/Mapper"; + public static final String APPEND_SIGNATURE = "(Ljava/lang/String;)Ljava/lang/StringBuilder;"; + public static final String OBJECT = "Ljava/lang/Object;"; + + public MapperFactory() { + super(ASM7); + init(); + } + + final ClassNode classNode = new ClassNode(); + + private String classToMap; + + private MethodNode jsonMethod; + + private boolean firstGetter = true; + + private final LinkedList getterInsnList = new LinkedList<>(); + + private void init() { + classNode.version = V1_5; + classNode.access = ACC_PUBLIC; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + this.classToMap = name; + classNode.name = "Mapper"+ UUID.randomUUID(); + classNode.superName = MAPPERBASECLASS; + MethodNode constructor = new MethodNode(ACC_PUBLIC, INIT, ZERO_ARGS_VOID, null, null); + constructor.instructions.add(new VarInsnNode(ALOAD, 0)); + constructor.instructions.add(new MethodInsnNode(INVOKESPECIAL, MAPPERBASECLASS, INIT, ZERO_ARGS_VOID)); + constructor.instructions.add(new InsnNode(RETURN)); + classNode.methods.add(constructor); + + jsonMethod = new MethodNode(ACC_PUBLIC, + "json", "(Ljava/lang/Object;)Ljava/lang/String;", null, null); + classNode.methods.add(jsonMethod); + add(new VarInsnNode(ALOAD, 1)); + add(new TypeInsnNode(CHECKCAST, name)); + add(new VarInsnNode(ASTORE, 1)); + add(new TypeInsnNode(NEW, STRINGBUILDER)); + add(new InsnNode(DUP)); + add(new MethodInsnNode(INVOKESPECIAL, STRINGBUILDER, "", "()V")); + add(new VarInsnNode(ASTORE, 2)); + add(new VarInsnNode(ALOAD, 2)); + add(new LdcInsnNode("{")); + add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE)); + add(new InsnNode(POP)); + } + + public MethodVisitor visitMethod(int access, String methodname, + String desc, String signature, String[] exceptions) { + if (!hasArgs(desc) && access == Modifier.PUBLIC && + (methodname.startsWith("get") || (methodname.startsWith("is")) && desc.equals("()Z"))) { + visitGetter(methodname, getReturnType(desc)); + } + return null; + } + + private void visitGetter(String getterMethodName, String returnType) { + int startIndex; + if (getterMethodName.startsWith("is")) { + startIndex = 2; + } else { + startIndex = 3; + } + + if (!firstGetter){ + getterInsnList.add(new VarInsnNode(ALOAD, 2)); + getterInsnList.add(new LdcInsnNode(",")); + getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE)); + getterInsnList.add(new InsnNode(POP)); + } else { + firstGetter = false; + } + + getterInsnList.add(new VarInsnNode(ALOAD, 2)); + getterInsnList.add(new LdcInsnNode("\"" + getterMethodName.substring(startIndex).toLowerCase() + "\":")); + getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE)); + getterInsnList.add(new InsnNode(POP)); + getterInsnList.add(new VarInsnNode(ALOAD, 2)); + getterInsnList.add(new VarInsnNode(ALOAD, 1)); + getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType)); + getterInsnList.add(new MethodInsnNode(INVOKESTATIC, MAPPER, "json", "(" + genericReturnType(returnType) + ")Ljava/lang/String;")); + getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE)); + getterInsnList.add(new InsnNode(POP)); + } + + private static String genericReturnType(String returnType) { + char firstChar = returnType.charAt(0); + if (firstChar == 'L' || firstChar == '[') { + return OBJECT; + } else { + return returnType; + } + } + + @Override + public void visitEnd() { + for (AbstractInsnNode insn : getterInsnList) { + add(insn); + } + add(new VarInsnNode(ALOAD, 2)); + add(new LdcInsnNode("}")); + add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE)); + add(new VarInsnNode(ALOAD, 2)); + add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, "toString", "()Ljava/lang/String;")); + add(new InsnNode(ARETURN)); + } + + private void add(AbstractInsnNode ins) { + jsonMethod.instructions.add(ins); + } + + private String getReturnType(String desc) { + return desc.substring(2); + } + + private boolean hasArgs(String desc) { + return desc.charAt(1) != ')'; + } + + private static String javaName(String className) { + return className.replaceAll("\\.", "/"); + } + +} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/StringPropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/StringPropertyTest.java deleted file mode 100644 index 47cb7b7..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/StringPropertyTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package nl.jssl.jsontoy.serialize; - -import static junit.framework.Assert.assertEquals; - -import org.junit.Test; - -public class StringPropertyTest { - - @Test - public void stringValue() { - assertEquals("\"value\"", Serializer.toJSONString("value")); - } - - @Test - public void stringProperty() { - Bean object = new Bean(); - object.setData1("value1"); - object.setData2("value2"); - assertEquals("{\"data1\": \"value1\",\"data2\": \"value2\"}", Serializer.toJSONString(object)); - } - - @Test - public void stringPropertyNull() { - Bean object = new Bean(); - object.setData1("value1"); - object.setData2("null"); - assertEquals("{\"data1\": \"value1\",\"data2\": \"null\"}", Serializer.toJSONString(object)); - } - - public class Bean { - private String data1; - private 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/jssl/jsontoy/serialize/collections/ArrayTest.java b/src/test/java/nl/jssl/jsontoy/serialize/collections/ArrayTest.java deleted file mode 100644 index f9a6669..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/collections/ArrayTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package nl.jssl.jsontoy.serialize.collections; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class ArrayTest { - @Test - public void testValue() { - assertEquals("[\"value1\", \"value2\"]", Serializer.toJSONString(new String[] { "value1", "value2" })); - } - - @Test - public void testPropertyValue() { - Bean object = new Bean(); - object.setArray(new String[] { "value1", "value2" }); - assertEquals("{\"array\": [\"value1\", \"value2\"]}", Serializer.toJSONString(object)); - } - - public class Bean { - private String[] array; - - public String[] getArray() { - return array; - } - - public void setArray(String[] array) { - this.array = array; - } - - } - -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/collections/ListTest.java b/src/test/java/nl/jssl/jsontoy/serialize/collections/ListTest.java deleted file mode 100644 index 2f18f2e..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/collections/ListTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package nl.jssl.jsontoy.serialize.collections; - -import static junit.framework.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class ListTest { - @Test - public void testValue() { - List list = new ArrayList(); - list.add("value1"); - list.add("value2"); - assertEquals("[\"value1\", \"value2\"]", Serializer.toJSONString(list)); - } - - @Test - public void testPropertyValue() { - Bean object = new Bean(); - object.setArray(new String[] { "value1", "value2" }); - assertEquals("{\"array\": [\"value1\", \"value2\"]}", Serializer.toJSONString(object)); - } - - public class Bean { - private String[] array; - - public String[] getArray() { - return array; - } - - public void setArray(String[] array) { - this.array = array; - } - - } - -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/collections/MapTest.java b/src/test/java/nl/jssl/jsontoy/serialize/collections/MapTest.java deleted file mode 100644 index c529dd7..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/collections/MapTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package nl.jssl.jsontoy.serialize.collections; - -import nl.jssl.jsontoy.serialize.Serializer; -import org.junit.Test; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertTrue; - -public class MapTest { - @Test - public void testValue() { - Map map = new HashMap<>(); - map.put("key1", "value1"); - map.put("key2", "value2"); - String jsonString = Serializer.toJSONString(map); - assertTrue("{\"key1\": \"value1\", \"key2\": \"value2\"}".equals(jsonString) || "{\"key2\": \"value2\", \"key1\": \"value1\"}".equals(jsonString)); - } - - @Test - public void testPropertyValue() { - Bean object = new Bean(); - Map map = new HashMap<>(); - map.put("key1", "value1"); - map.put("key2", "value2"); - object.setMap(map); - String jsonString = Serializer.toJSONString(object); - assertTrue("{\"map\": {\"key1\": \"value1\", \"key2\": \"value2\"}}".equals(jsonString) - || "{\"map\": {\"key2\": \"value2\", \"key1\": \"value1\"}}".equals(jsonString)); - } - - public class Bean { - - private Map map; - - public Map getMap() { - return map; - } - - public void setMap(Map map) { - this.map = map; - } - - } - -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/collections/SetTest.java b/src/test/java/nl/jssl/jsontoy/serialize/collections/SetTest.java deleted file mode 100644 index 4f27072..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/collections/SetTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package nl.jssl.jsontoy.serialize.collections; - -import static junit.framework.Assert.assertTrue; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class SetTest { - @Test - public void testValue() { - Set list = new HashSet(); - list.add("value1"); - list.add("value2"); - String jsonString = Serializer.toJSONString(list); - assertTrue("[\"value2\", \"value1\"]".equals(jsonString) || "[\"value1\", \"value2\"]".equals(jsonString)); - } - - @Test - public void testPropertyValue() { - Bean object = new Bean(); - object.setSet(new HashSet(Arrays.asList("value1", "value2"))); - String jsonString = Serializer.toJSONString(object); - assertTrue("{\"set\": [\"value2\", \"value1\"]}".equals(jsonString) || "{\"set\": [\"value1\", \"value2\"]}".equals(jsonString)); - } - - public class Bean { - private Set set; - - public Set getSet() { - return set; - } - - public void setSet(Set set) { - this.set = set; - } - } - -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/nested/Bean1.java b/src/test/java/nl/jssl/jsontoy/serialize/nested/Bean1.java deleted file mode 100644 index cc5e5cf..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/nested/Bean1.java +++ /dev/null @@ -1,48 +0,0 @@ -package nl.jssl.jsontoy.serialize.nested; - -public class Bean1 { - private String data1; - private Bean2 bean2; - - public String getData1() { - return data1; - } - - public void setData1(String data1) { - this.data1 = data1; - } - - public Bean2 getBean2() { - return bean2; - } - - public void setBean2(Bean2 bean2) { - this.bean2 = bean2; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((data1 == null) ? 0 : data1.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Bean1 other = (Bean1) obj; - if (data1 == null) { - if (other.data1 != null) - return false; - } else if (!data1.equals(other.data1)) - return false; - return true; - } - -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/nested/Bean2.java b/src/test/java/nl/jssl/jsontoy/serialize/nested/Bean2.java deleted file mode 100644 index a894418..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/nested/Bean2.java +++ /dev/null @@ -1,13 +0,0 @@ -package nl.jssl.jsontoy.serialize.nested; - -public class Bean2 { - private String data2; - - public String getData2() { - return data2; - } - - public void setData2(String data2) { - this.data2 = data2; - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/nested/Bean2Child.java b/src/test/java/nl/jssl/jsontoy/serialize/nested/Bean2Child.java deleted file mode 100644 index e0d62b2..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/nested/Bean2Child.java +++ /dev/null @@ -1,14 +0,0 @@ -package nl.jssl.jsontoy.serialize.nested; - -public class Bean2Child extends Bean2 { - private String data3; - - public String getData3() { - return data3; - } - - public void setData3(String data3) { - this.data3 = data3; - } - -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/nested/NestedBeanTest.java b/src/test/java/nl/jssl/jsontoy/serialize/nested/NestedBeanTest.java deleted file mode 100644 index febff28..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/nested/NestedBeanTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package nl.jssl.jsontoy.serialize.nested; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class NestedBeanTest { - @Test - public void testBeans() { - Bean1 object1 = new Bean1(); - object1.setData1("value1"); - Bean2 object2 = new Bean2(); - object2.setData2("value2"); - object1.setBean2(object2); - assertEquals("{\"data1\": \"value1\",\"bean2\": {\"data2\": \"value2\"}}", Serializer.toJSONString(object1)); - assertEquals("{\"data1\": \"value1\",\"bean2\": {\"data2\": \"value2\"}}", Serializer.toJSONString(object1)); - } - - @Test - public void testNullValueForBean() { - Bean1 object1 = new Bean1(); - object1.setData1("value1"); - assertEquals("{\"data1\": \"value1\",\"bean2\": null}", Serializer.toJSONString(object1)); - } - - @Test - public void testBeanChildren() { - Bean1 object1 = new Bean1(); - object1.setData1("value1"); - Bean2Child object3 = new Bean2Child(); - object3.setData2("value2"); - object3.setData3("value3"); - object1.setBean2(object3); - assertEquals("{\"data1\": \"value1\",\"bean2\": {\"data3\": \"value3\",\"data2\": \"value2\"}}", Serializer.toJSONString(object1)); - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/primitives/BooleanPropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/primitives/BooleanPropertyTest.java deleted file mode 100644 index e4ada75..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/primitives/BooleanPropertyTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package nl.jssl.jsontoy.serialize.primitives; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class BooleanPropertyTest { - - @Test - public void testPrimitive() { - assertEquals("false", Serializer.toJSONString(false)); - } - - @Test - public void testWrapper() { - assertEquals("false", Serializer.toJSONString(Boolean.FALSE)); - } - - @Test - public void testProperty() { - Bean object = new Bean(); - object.setData(true); - assertEquals("{\"data\": true}", Serializer.toJSONString(object)); - } - - public class Bean { - private boolean data; - - public boolean getData() { - return data; - } - - public void setData(boolean data) { - this.data = data; - } - - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/primitives/BytePropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/primitives/BytePropertyTest.java deleted file mode 100644 index 4ef7d17..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/primitives/BytePropertyTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package nl.jssl.jsontoy.serialize.primitives; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class BytePropertyTest { - @Test - public void testPrimitive() { - assertEquals("-55", Serializer.toJSONString((byte) -55)); - } - - @Test - public void testWrapper() { - assertEquals("55", Serializer.toJSONString((byte) 55)); - } - - @Test - public void testProperty() { - Bean object = new Bean(); - object.setData((byte) 1); - assertEquals("{\"data\": 1}", Serializer.toJSONString(object)); - } - - public class Bean { - private byte data; - - public byte getData() { - return data; - } - - public void setData(byte data) { - this.data = data; - } - - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/primitives/CharPropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/primitives/CharPropertyTest.java deleted file mode 100644 index 32c1090..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/primitives/CharPropertyTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package nl.jssl.jsontoy.serialize.primitives; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class CharPropertyTest { - @Test - public void testPrimitive() { - assertEquals("\"d\"", Serializer.toJSONString('d')); - } - - @Test - public void testWrapper() { - assertEquals("\"s\"", Serializer.toJSONString('s')); - } - - @Test - public void testProperty() { - Bean object = new Bean(); - object.setData('a'); - assertEquals("{\"data\": \"a\"}", Serializer.toJSONString(object)); - } - - public class Bean { - private char data; - - public char getData() { - return data; - } - - public void setData(char data) { - this.data = data; - } - - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/primitives/DoublePropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/primitives/DoublePropertyTest.java deleted file mode 100644 index 889f9a3..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/primitives/DoublePropertyTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package nl.jssl.jsontoy.serialize.primitives; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class DoublePropertyTest { - @Test - public void testPrimitive() { - assertEquals("-55.6", Serializer.toJSONString(-55.6D)); - } - - @Test - public void testWrapper() { - assertEquals("55.0", Serializer.toJSONString(Double.valueOf("55.0"))); - } - - @Test - public void testProperty() { - Bean object = new Bean(); - object.setData(326.2D); - assertEquals("{\"data\": 326.2}", Serializer.toJSONString(object)); - } - - public class Bean { - private double data; - - public double getData() { - return data; - } - - public void setData(double data) { - this.data = data; - } - - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/primitives/FloatPropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/primitives/FloatPropertyTest.java deleted file mode 100644 index 9797c31..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/primitives/FloatPropertyTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package nl.jssl.jsontoy.serialize.primitives; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class FloatPropertyTest { - @Test - public void testPrimitive() { - assertEquals("-55.6", Serializer.toJSONString(-55.6F)); - } - - @Test - public void testWrapper() { - assertEquals("55.0", Serializer.toJSONString(Float.valueOf("55.0"))); - } - - @Test - public void testProperty() { - Bean object = new Bean(); - object.setData(1F); - assertEquals("{\"data\": 1.0}", Serializer.toJSONString(object)); - } - - public class Bean { - private float data; - - public float getData() { - return data; - } - - public void setData(float data) { - this.data = data; - } - - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/primitives/IntPropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/primitives/IntPropertyTest.java deleted file mode 100644 index ffe60b8..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/primitives/IntPropertyTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package nl.jssl.jsontoy.serialize.primitives; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class IntPropertyTest { - - @Test - public void testPrimitive() { - assertEquals("-55", Serializer.toJSONString(-55)); - } - - @Test - public void testWrapper() { - assertEquals("55", Serializer.toJSONString(Integer.valueOf("55"))); - } - - @Test - public void testPropertyValue() { - Bean object = new Bean(); - object.setData(1); - assertEquals("{\"data\": 1}", Serializer.toJSONString(object)); - } - - public class Bean { - private int data; - - public int getData() { - return data; - } - - public void setData(int data) { - this.data = data; - } - - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/primitives/LongPropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/primitives/LongPropertyTest.java deleted file mode 100644 index 629fc00..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/primitives/LongPropertyTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package nl.jssl.jsontoy.serialize.primitives; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class LongPropertyTest { - @Test - public void testPrimitive() { - assertEquals("-55", Serializer.toJSONString(-55L)); - } - - @Test - public void testWrapper() { - assertEquals("55", Serializer.toJSONString(Long.valueOf("55"))); - } - - @Test - public void testProperty() { - Bean object = new Bean(); - object.setData(1L); - assertEquals("{\"data\": 1}", Serializer.toJSONString(object)); - } - - public class Bean { - private long data; - - public long getData() { - return data; - } - - public void setData(long data) { - this.data = data; - } - - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/primitives/NullPropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/primitives/NullPropertyTest.java deleted file mode 100644 index 300a641..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/primitives/NullPropertyTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package nl.jssl.jsontoy.serialize.primitives; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class NullPropertyTest { - - @Test - public void testPrimitive() { - assertEquals("null", Serializer.toJSONString(null)); - } - - @Test - public void testProperty() { - Bean object = new Bean(); - assertEquals("{\"data\": null}", Serializer.toJSONString(object)); - } - - public class Bean { - private String data; - - public String getData() { - return data; - } - - public void setData(String data) { - this.data = data; - } - - } -} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/primitives/ShortPropertyTest.java b/src/test/java/nl/jssl/jsontoy/serialize/primitives/ShortPropertyTest.java deleted file mode 100644 index aaefa72..0000000 --- a/src/test/java/nl/jssl/jsontoy/serialize/primitives/ShortPropertyTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package nl.jssl.jsontoy.serialize.primitives; - -import static junit.framework.Assert.assertEquals; -import nl.jssl.jsontoy.serialize.Serializer; - -import org.junit.Test; - -public class ShortPropertyTest { - @Test - public void testPrimitive() { - assertEquals("-55", Serializer.toJSONString((short) -55)); - } - - @Test - public void testWrapper() { - assertEquals("5", Serializer.toJSONString(Short.valueOf("5"))); - } - - @Test - public void testProperty() { - Bean object = new Bean(); - object.setData((short) 3); - assertEquals("{\"data\": 3}", Serializer.toJSONString(object)); - } - - public class Bean { - private short data; - - public short getData() { - return data; - } - - public void setData(short data) { - this.data = data; - } - - } -} diff --git a/src/test/java/nl/sanderhautvast/json/ser/ByteClassLoaderTest.java b/src/test/java/nl/sanderhautvast/json/ser/ByteClassLoaderTest.java new file mode 100644 index 0000000..c28c172 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/ByteClassLoaderTest.java @@ -0,0 +1,14 @@ +package nl.sanderhautvast.json.ser; + + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ByteClassLoaderTest { + + @Test + public void foo() { + assertThrows(ClassNotFoundException.class, () -> new ByteClassLoader().findClass("WrongName")); + } +} \ No newline at end of file diff --git a/src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java new file mode 100644 index 0000000..eca42db --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java @@ -0,0 +1,37 @@ +package nl.sanderhautvast.json.ser; + +import lombok.Data; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class StringPropertyTest { + + @Test + public void stringValue() { + assertEquals("\"value\"", Mapper.json("value")); + } + + @Test + public void stringProperty() { + Bean object = new Bean(); + object.setData1("value1"); + object.setData2("value2"); + assertEquals("{\"data1\":\"value1\",\"data2\":\"value2\"}", Mapper.json(object)); + } + + @Test + public void stringPropertyNull() { + Bean object = new Bean(); + object.setData1("value1"); + object.setData2(null); + assertEquals("{\"data1\":\"value1\",\"data2\":null}", Mapper.json(object)); + } + + @Data + public static class Bean { + private String data1; + private String data2; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/collections/ArrayTest.java b/src/test/java/nl/sanderhautvast/json/ser/collections/ArrayTest.java new file mode 100644 index 0000000..3172529 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/collections/ArrayTest.java @@ -0,0 +1,28 @@ +package nl.sanderhautvast.json.ser.collections; + + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ArrayTest { + @Test + public void testValue() { + assertEquals("[\"value1\",\"value2\"]", Mapper.json(new String[]{"value1", "value2"})); + } + + @Test + public void testPropertyValue() { + Bean object = new Bean(); + object.setArray(new String[]{"value1", "value2"}); + assertEquals("{\"array\":[\"value1\",\"value2\"]}", Mapper.json(object)); + } + + @Data + public static class Bean { + private String[] array; + } + +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java b/src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java new file mode 100644 index 0000000..a49b36d --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java @@ -0,0 +1,36 @@ +package nl.sanderhautvast.json.ser.collections; + + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ListTest { + + @Test + void testEmpty() { + assertEquals("[]", Mapper.json(List.of())); + } + + @Test + void testValue() { + assertEquals("[\"value1\",\"value2\"]", Mapper.json(List.of("value1", "value2"))); + } + + @Test + void testPropertyValue() { + Bean object = new Bean(); + object.setList(List.of("value1", "value2")); + assertEquals("{\"list\":[\"value1\",\"value2\"]}", Mapper.json((object))); + } + + @Data + public static class Bean { + private List list; + } + +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java b/src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java new file mode 100644 index 0000000..2379c49 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java @@ -0,0 +1,47 @@ +package nl.sanderhautvast.json.ser.collections; + + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +public class MapTest { + @Test + public void testValue() { + String jsonString = Mapper.json(Map.of("key1", "value1", "key2", "value2")); + assertTrue("{\"key1\":\"value1\",\"key2\":\"value2\"}".equals(jsonString) + || "{\"key2\":\"value2\",\"key1\":\"value1\"}".equals(jsonString), jsonString); //order doesn't matter + } + + @Test + public void testIntegerKeys() { + String jsonString = Mapper.json(Map.of(1, "value1", 2, "value2")); + assertTrue("{\"1\":\"value1\",\"2\":\"value2\"}".equals(jsonString) + || "{\"2\":\"value2\",\"1\":\"value1\"}".equals(jsonString), jsonString); //order doesn't matter + } + + @Test + void testEmpty(){ + assertEquals("{}", Mapper.json(Map.of())); + } + + @Test + public void testPropertyValue() { + Bean object = new Bean(); + object.setMap(Map.of("key1", "value1", "key2", "value2")); + String jsonString = Mapper.json(object); + assertTrue("{\"map\":{\"key1\":\"value1\",\"key2\":\"value2\"}}".equals(jsonString) + || "{\"map\":{\"key2\":\"value2\",\"key1\":\"value1\"}}".equals(jsonString), jsonString); + } + + @Data + public static class Bean { + private Map map; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java b/src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java new file mode 100644 index 0000000..cab57d8 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java @@ -0,0 +1,38 @@ +package nl.sanderhautvast.json.ser.collections; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SetTest { + + @Test + void testEmpty(){ + assertEquals("[]", Mapper.json(Set.of())); + } + @Test + public void testValue() { + String jsonString = Mapper.json(Set.of("value1", "value2")); + assertTrue("[\"value2\",\"value1\"]".equals(jsonString) + || "[\"value1\",\"value2\"]".equals(jsonString), jsonString); + } + + @Test + public void testPropertyValue() { + Bean object = new Bean(); + object.setSet(Set.of("value1", "value2")); + String jsonString = Mapper.json(object); + assertTrue("{\"set\":[\"value2\",\"value1\"]}".equals(jsonString) + || "{\"set\":[\"value1\",\"value2\"]}".equals(jsonString)); + } + + @Data + public static class Bean { + private Set set; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/collections/StringTests.java b/src/test/java/nl/sanderhautvast/json/ser/collections/StringTests.java new file mode 100644 index 0000000..f8bec04 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/collections/StringTests.java @@ -0,0 +1,44 @@ +package nl.sanderhautvast.json.ser.collections; + +import com.fasterxml.jackson.core.JsonProcessingException; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class StringTests { + + @Test + void testCharEscapes() { + assertEquals("\"\\t\"", Mapper.json('\t')); + assertEquals("\"\\b\"", Mapper.json('\b')); + assertEquals("\"\\r\"", Mapper.json('\r')); + assertEquals("\"\\n\"", Mapper.json('\n')); + assertEquals("\"\\\"\"", Mapper.json('\"')); + assertEquals("\"\\\\\"", Mapper.json('\\')); + assertEquals("\"\\f\"", Mapper.json('\f')); + assertEquals("\"\\/\"", Mapper.json('/')); + assertEquals("\"'\"", Mapper.json('\'')); + } + + @Test + void testStringEscapes() { + assertEquals("\"bla\\tbla\"", Mapper.json("bla\tbla")); + assertEquals("\"\\b\\b\"", Mapper.json("\b\b")); + assertEquals("\"\\r\\n\"", Mapper.json("\r\n")); + assertEquals("\"\\n\"", Mapper.json("\n")); + assertEquals("\"\\\"\"", Mapper.json("\"")); + assertEquals("\"\\\\\"", Mapper.json("\\")); + assertEquals("\"\\f\"", Mapper.json("\f")); + assertEquals("\"\\/\"", Mapper.json("/")); + assertEquals("\"'\"", Mapper.json("'")); + } + + @Test + void unicodeTests() { + assertEquals("\"angry=๐Ÿ˜ \"", Mapper.json("angry=๐Ÿ˜ ")); + assertEquals("\"\\u20A7dings\"", Mapper.json("โ‚งdings")); + assertEquals("\"็‹—=dog\"", Mapper.json("็‹—=dog")); // dog + } + +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java b/src/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java new file mode 100644 index 0000000..3db19de --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java @@ -0,0 +1,10 @@ +package nl.sanderhautvast.json.ser.nested; + + +import lombok.Data; + +@Data +public class Bean1 { + private String data1; + private Bean2 bean2; +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/nested/Bean2.java b/src/test/java/nl/sanderhautvast/json/ser/nested/Bean2.java new file mode 100644 index 0000000..de08b97 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/nested/Bean2.java @@ -0,0 +1,9 @@ +package nl.sanderhautvast.json.ser.nested; + + +import lombok.Data; + +@Data +public class Bean2 { + private String data2; +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/nested/NestedBeanTest.java b/src/test/java/nl/sanderhautvast/json/ser/nested/NestedBeanTest.java new file mode 100644 index 0000000..e628406 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/nested/NestedBeanTest.java @@ -0,0 +1,37 @@ +//package nl.jssl.jsontoy.serialize.nested; +// +//import static junit.framework.Assert.assertEquals; +//import nl.jssl.jsontoy.serialize.Serializer; +// +//import org.junit.Test; +// +//public class NestedBeanTest { +// @Test +// public void testBeans() { +// Bean1 object1 = new Bean1(); +// object1.setData1("value1"); +// Bean2 object2 = new Bean2(); +// object2.setData2("value2"); +// object1.setBean2(object2); +// assertEquals("{\"data1\": \"value1\",\"bean2\": {\"data2\": \"value2\"}}", Serializer.toJSONString(object1)); +// assertEquals("{\"data1\": \"value1\",\"bean2\": {\"data2\": \"value2\"}}", Serializer.toJSONString(object1)); +// } +// +// @Test +// public void testNullValueForBean() { +// Bean1 object1 = new Bean1(); +// object1.setData1("value1"); +// assertEquals("{\"data1\": \"value1\",\"bean2\": null}", Serializer.toJSONString(object1)); +// } +// +// @Test +// public void testBeanChildren() { +// Bean1 object1 = new Bean1(); +// object1.setData1("value1"); +// Bean2Child object3 = new Bean2Child(); +// object3.setData2("value2"); +// object3.setData3("value3"); +// object1.setBean2(object3); +// assertEquals("{\"data1\": \"value1\",\"bean2\": {\"data3\": \"value3\",\"data2\": \"value2\"}}", Serializer.toJSONString(object1)); +// } +//} diff --git a/src/test/java/nl/jssl/jsontoy/serialize/performance/JacksonComparisonTest.java b/src/test/java/nl/sanderhautvast/json/ser/performance/JacksonComparisonTest.java similarity index 82% rename from src/test/java/nl/jssl/jsontoy/serialize/performance/JacksonComparisonTest.java rename to src/test/java/nl/sanderhautvast/json/ser/performance/JacksonComparisonTest.java index 8daaa5b..46e9364 100644 --- a/src/test/java/nl/jssl/jsontoy/serialize/performance/JacksonComparisonTest.java +++ b/src/test/java/nl/sanderhautvast/json/ser/performance/JacksonComparisonTest.java @@ -1,22 +1,21 @@ -package nl.jssl.jsontoy.serialize.performance; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import nl.jssl.jsontoy.serialize.Serializer; -import nl.jssl.jsontoy.serialize.nested.Bean1; -import nl.jssl.jsontoy.serialize.nested.Bean2; - -import org.junit.Test; +package nl.sanderhautvast.json.ser.performance; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import nl.sanderhautvast.json.ser.Mapper; +import nl.sanderhautvast.json.ser.nested.Bean1; +import nl.sanderhautvast.json.ser.nested.Bean2; +import org.junit.jupiter.api.Test; +import java.util.*; + +/* + * => about 10% faster than jackson + */ public class JacksonComparisonTest { private static final int ITERATIONS = 20; private static final int INNERLOOP_COUNT = 100000; - List trashbin = new ArrayList<>(); + private final List trashbin = new ArrayList<>(); @Test public void testPerformance() throws JsonProcessingException { @@ -52,7 +51,7 @@ public class JacksonComparisonTest { bean1.setData1(UUID.randomUUID().toString()); bean1.setBean2(bean2); bean2.setData2(UUID.randomUUID().toString()); - jsonString = Serializer.toJSONString(bean1); + jsonString = Mapper.json(bean1); trashbin.add(jsonString); } System.out.printf("% 7d%n",System.currentTimeMillis() - tt0); diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java new file mode 100644 index 0000000..cf35eee --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java @@ -0,0 +1,32 @@ +package nl.sanderhautvast.json.ser.primitives; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BooleanPropertyTest { + + @Test + public void testPrimitive() { + assertEquals("true", Mapper.json(true)); + } + + @Test + public void testWrapper() { + assertEquals("true", Mapper.json(Boolean.TRUE)); + } + + @Test + public void testProperty() { + Bean object = new Bean(); + object.setData(true); + assertEquals("{\"data\":true}", Mapper.json(object)); + } + + @Data + public static class Bean { + boolean data; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java new file mode 100644 index 0000000..acc4176 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java @@ -0,0 +1,31 @@ +package nl.sanderhautvast.json.ser.primitives; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BytePropertyTest { + @Test + public void testPrimitive() { + assertEquals("-55", Mapper.json((byte) -55)); + } + + @Test + public void testWrapper() { + assertEquals("55", Mapper.json(Byte.valueOf((byte) 55))); + } + + @Test + public void testProperty() { + Bean object = new Bean(); + object.setData((byte) 1); + assertEquals("{\"data\":1}", Mapper.json(object)); + } + + @Data + public static class Bean { + private byte data; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/CharPropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/primitives/CharPropertyTest.java new file mode 100644 index 0000000..e3b6164 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/primitives/CharPropertyTest.java @@ -0,0 +1,31 @@ +package nl.sanderhautvast.json.ser.primitives; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CharPropertyTest { + @Test + public void testPrimitive() { + assertEquals("\"d\"", Mapper.json('d')); + } + + @Test + public void testWrapper() { + assertEquals("\"s\"", Mapper.json(Character.valueOf('s'))); + } + + @Test + public void testProperty() { + Bean object = new Bean(); + object.setData('a'); + assertEquals("{\"data\":\"a\"}", Mapper.json(object)); + } + + @Data + public static class Bean { + private char data; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/DoublePropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/primitives/DoublePropertyTest.java new file mode 100644 index 0000000..891ef72 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/primitives/DoublePropertyTest.java @@ -0,0 +1,31 @@ +package nl.sanderhautvast.json.ser.primitives; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DoublePropertyTest { + @Test + public void testPrimitive() { + assertEquals("-55.6", Mapper.json(-55.6D)); + } + + @Test + public void testWrapper() { + assertEquals("55.0", Mapper.json(Double.valueOf("55.0"))); + } + + @Test + public void testProperty() { + Bean object = new Bean(); + object.setData(326.2D); + assertEquals("{\"data\":326.2}", Mapper.json(object)); + } + + @Data + public static class Bean { + private double data; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java new file mode 100644 index 0000000..d563746 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java @@ -0,0 +1,31 @@ +package nl.sanderhautvast.json.ser.primitives; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FloatPropertyTest { + @Test + public void testPrimitive() { + assertEquals("-55.6", Mapper.json(-55.6F)); + } + + @Test + public void testWrapper() { + assertEquals("55.0", Mapper.json(Float.valueOf("55.0"))); + } + + @Test + public void testProperty() { + Bean object = new Bean(); + object.setData(1F); + assertEquals("{\"data\":1.0}", Mapper.json(object)); + } + + @Data + public static class Bean { + private float data; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/IntPropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/primitives/IntPropertyTest.java new file mode 100644 index 0000000..a692b6f --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/primitives/IntPropertyTest.java @@ -0,0 +1,32 @@ +package nl.sanderhautvast.json.ser.primitives; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class IntPropertyTest { + + @Test + public void testPrimitive() { + assertEquals("-55", Mapper.json(-55)); + } + + @Test + public void testWrapper() { + assertEquals("55", Mapper.json(Integer.valueOf("55"))); + } + + @Test + public void testPropertyValue() { + Bean object = new Bean(); + object.setData(1); + assertEquals("{\"data\":1}", Mapper.json(object)); + } + + @Data + public static class Bean { + int data; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java new file mode 100644 index 0000000..bb35e85 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java @@ -0,0 +1,32 @@ +package nl.sanderhautvast.json.ser.primitives; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class LongPropertyTest { + @Test + public void testPrimitive() { + assertEquals("-55", Mapper.json(-55L)); + } + + @Test + public void testWrapper() { + assertEquals("55", Mapper.json(Long.valueOf("55"))); + } + + @Test + public void testProperty() { + Bean object = new Bean(); + object.setData(1L); + assertEquals("{\"data\":1}", Mapper.json(object)); + } + + @Data + public static class Bean { + private long data; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/NullPropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/primitives/NullPropertyTest.java new file mode 100644 index 0000000..f7a9cdf --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/primitives/NullPropertyTest.java @@ -0,0 +1,21 @@ +package nl.sanderhautvast.json.ser.primitives; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class NullPropertyTest { + + @Test + public void testProperty() { + Bean object = new Bean(); + assertEquals("{\"data\":null}", Mapper.json(object)); + } + + @Data + public static class Bean { + private String data; + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/ShortPropertyTest.java b/src/test/java/nl/sanderhautvast/json/ser/primitives/ShortPropertyTest.java new file mode 100644 index 0000000..1aef1a6 --- /dev/null +++ b/src/test/java/nl/sanderhautvast/json/ser/primitives/ShortPropertyTest.java @@ -0,0 +1,31 @@ +package nl.sanderhautvast.json.ser.primitives; + +import lombok.Data; +import nl.sanderhautvast.json.ser.Mapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ShortPropertyTest { + @Test + public void testPrimitive() { + assertEquals("-55", Mapper.json((short) -55)); + } + + @Test + public void testWrapper() { + assertEquals("5", Mapper.json(Short.valueOf("5"))); + } + + @Test + public void testProperty() { + Bean object = new Bean(); + object.setData((short) 3); + assertEquals("{\"data\":3}", Mapper.json(object)); + } + + @Data + public static class Bean { + private short data; + } +}