diff --git a/.gitignore b/.gitignore index 6806942..0e52fcc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ /.classpath /.settings/ /.idea/ -/target/ +target/ /*.iml diff --git a/jmh/pom.xml b/jmh/pom.xml new file mode 100644 index 0000000..2a3a222 --- /dev/null +++ b/jmh/pom.xml @@ -0,0 +1,98 @@ + + 4.0.0 + + + nl.sander + jsonthingy-pom + 0.1-SNAPSHOT + + + JsonToy-JMH + jsonthingy-jmhtests + 0.1-SNAPSHOT + jar + + + 9 + 9 + UTF-8 + + + + + + ${project.groupId} + jsonthingy + ${project.version} + + + com.fasterxml.jackson.core + jackson-databind + 2.15.1 + + + + org.openjdk.jmh + jmh-core + 1.36 + + + org.openjdk.jmh + jmh-generator-annprocess + 1.36 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 9 + 9 + 9 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + benchmark + + + org.openjdk.jmh.Main + + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + diff --git a/jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean1.java b/jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean1.java new file mode 100644 index 0000000..e662f50 --- /dev/null +++ b/jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean1.java @@ -0,0 +1,22 @@ +package nl.sanderhautvast.json.jmh; + +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; + } +} diff --git a/jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean2.java b/jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean2.java new file mode 100644 index 0000000..5668c45 --- /dev/null +++ b/jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean2.java @@ -0,0 +1,13 @@ +package nl.sanderhautvast.json.jmh; + +public class Bean2 { + private String data2; + + public String getData2() { + return data2; + } + + public void setData2(String data2) { + this.data2 = data2; + } +} diff --git a/jmh/src/main/java/nl/sanderhautvast/json/jmh/Benchmarks.java b/jmh/src/main/java/nl/sanderhautvast/json/jmh/Benchmarks.java new file mode 100644 index 0000000..271cde1 --- /dev/null +++ b/jmh/src/main/java/nl/sanderhautvast/json/jmh/Benchmarks.java @@ -0,0 +1,48 @@ +package nl.sanderhautvast.json.jmh; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import nl.sanderhautvast.json.ser.Mapper; +import org.openjdk.jmh.annotations.*; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class Benchmarks { + + private static final int ITERATIONS = 10; + + @Benchmark + public void testJson() { + Bean1 bean1; + Bean2 bean2; + + for (int i = 0; i < ITERATIONS; i++) { + bean1 = new Bean1(); + bean2 = new Bean2(); + bean1.setData1(UUID.randomUUID().toString()); + bean1.setBean2(bean2); + bean2.setData2(UUID.randomUUID().toString()); + Mapper.json(bean1); + } + } + + @Benchmark + public void testJackson() throws JsonProcessingException { + Bean1 bean1; + Bean2 bean2; + ObjectMapper mapper = new ObjectMapper(); + for (int i = 0; i < ITERATIONS; i++) { + bean1 = new Bean1(); + bean2 = new Bean2(); + bean1.setData1(UUID.randomUUID().toString()); + bean1.setBean2(bean2); + bean2.setData2(UUID.randomUUID().toString()); + mapper.writeValueAsString(bean1); + } + } + +} diff --git a/lib/pom.xml b/lib/pom.xml new file mode 100644 index 0000000..aba672e --- /dev/null +++ b/lib/pom.xml @@ -0,0 +1,74 @@ + + 4.0.0 + + + nl.sander + jsonthingy-pom + 0.1-SNAPSHOT + + + JsonToy + jsonthingy + 0.1-SNAPSHOT + jar + + + 9 + 9 + UTF-8 + + + + + + org.ow2.asm + asm-tree + 9.4 + + + org.junit.jupiter + junit-jupiter + 5.9.3 + test + + + + org.mockito + mockito-all + 1.10.19 + test + + + + com.fasterxml.jackson.core + jackson-databind + 2.15.1 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.0 + + --add-opens java.base/java.lang=ALL-UNNAMED + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 9 + 9 + 9 + + + + + + diff --git a/src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java b/lib/src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java similarity index 56% rename from src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java rename to lib/src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java index b383f5a..e5197e5 100644 --- a/src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java +++ b/lib/src/main/java/nl/sanderhautvast/json/ser/BaseMapper.java @@ -2,6 +2,6 @@ package nl.sanderhautvast.json.ser; public abstract class BaseMapper { - protected abstract String json(T value); + protected abstract void json(StringBuilder b, T value); } diff --git a/src/main/java/nl/sanderhautvast/json/ser/ByteClassLoader.java b/lib/src/main/java/nl/sanderhautvast/json/ser/ByteClassLoader.java similarity index 100% rename from src/main/java/nl/sanderhautvast/json/ser/ByteClassLoader.java rename to lib/src/main/java/nl/sanderhautvast/json/ser/ByteClassLoader.java diff --git a/src/main/java/nl/sanderhautvast/json/ser/JsonError.java b/lib/src/main/java/nl/sanderhautvast/json/ser/JsonError.java similarity index 100% rename from src/main/java/nl/sanderhautvast/json/ser/JsonError.java rename to lib/src/main/java/nl/sanderhautvast/json/ser/JsonError.java diff --git a/lib/src/main/java/nl/sanderhautvast/json/ser/Mapper.java b/lib/src/main/java/nl/sanderhautvast/json/ser/Mapper.java new file mode 100644 index 0000000..f7de7ac --- /dev/null +++ b/lib/src/main/java/nl/sanderhautvast/json/ser/Mapper.java @@ -0,0 +1,473 @@ +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(); + private static final StringMapper stringMapper = new StringMapper(); + private static final BooleanMapper booleanMapper = new BooleanMapper(); + + private static final IntegerMapper integerMapper = new IntegerMapper(); + private static final LongMapper longMapper = new LongMapper(); + private static final ShortMapper shortMapper = new ShortMapper(); + private static final ByteMapper byteMapper = new ByteMapper(); + private static final CharMapper charMapper = new CharMapper(); + private static final FloatMapper floatMapper = new FloatMapper(); + private static final DoubleMapper doubleMapper = 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 + */ + public static String json(Object value) { + StringBuilder b = new StringBuilder(128); + json(b, value); + return b.toString(); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void json(StringBuilder b, Object value) { + if (value == null) { + b.append("null"); + } else { + Class type = value.getClass(); + if (type.isArray()) { + if (value instanceof byte[]) { + array(b, (byte[]) value); + } else if (value instanceof int[]) { + array(b, (int[]) value); + } else if (value instanceof short[]) { + array(b, (short[]) value); + } else if (value instanceof boolean[]) { + array(b, (boolean[]) value); + } else if (value instanceof char[]) { + array(b, (char[]) value); + } else if (value instanceof long[]) { + array(b, (long[]) value); + } else if (value instanceof float[]) { + array(b, (float[]) value); + } else if (value instanceof double[]) { + array(b, (double[]) value); + } else { + array(b, (Object[]) value); + } + } else if (value instanceof Collection) { + list(b, (Collection) value); + } else if (value instanceof Map) { + object(b, (Map) value); + } else { + if (type.equals(String.class)) { + stringMapper.json(b, (String) value); + } else if (type.equals(Boolean.class)) { + + booleanMapper.json(b, (Boolean) value); + } else if (type.equals(Integer.class)) { + integerMapper.json(b, (Integer) value); + } else if (type.equals(Long.class)) { + longMapper.json(b, (Long) value); + } else if (type.equals(Short.class)) { + shortMapper.json(b, (Short) value); + } else if (type.equals(Byte.class)) { + byteMapper.json(b, (Byte) value); + } else if (type.equals(Character.class)) { + charMapper.json(b, (Character) value); + } else if (type.equals(Float.class)) { + floatMapper.json(b, (Float) value); + } else if (type.equals(Double.class)) { + doubleMapper.json(b, (Double) value); + } else { + BaseMapper mapper = mappers.computeIfAbsent(type, key -> createObjectMapper(type)); + mapper.json(b, value); + } + } + } + } + + private static void array(StringBuilder b, Object[] array) { + if (array.length == 0) { + b.append("[]"); + } else { + Object first = array[0]; + b.append("["); + Mapper.json(b, first); + Arrays.stream(array).skip(1) + .forEach(element -> { + b.append(","); + Mapper.json(b, element); + }); + b.append("]"); + } + } + + private static void array(StringBuilder b, byte[] array) { + if (array.length == 0) { + b.append("[]"); + } else { + byte first = array[0]; + b.append("["); + json(b, first); + for (int i = 1; i < array.length; i++) { + b.append(","); + json(b, array[i]); + } + b.append("]"); + } + } + + private static void array(StringBuilder b, short[] array) { + if (array.length == 0) { + b.append("[]"); + } else { + short first = array[0]; + b.append("["); + json(b, first); + for (int i = 1; i < array.length; i++) { + b.append(","); + json(b, array[i]); + } + b.append("]"); + } + } + + private static void array(StringBuilder b, long[] array) { + if (array.length == 0) { + b.append("[]"); + } else { + long first = array[0]; + b.append("["); + json(b, first); + for (int i = 1; i < array.length; i++) { + b.append(","); + json(b, array[i]); + } + b.append("]"); + } + } + + private static void array(StringBuilder b, boolean[] array) { + if (array.length == 0) { + b.append("[]"); + } else { + boolean first = array[0]; + b.append("["); + json(b, first); + for (int i = 1; i < array.length; i++) { + b.append(","); + json(b, array[i]); + } + b.append("]"); + } + } + + private static void array(StringBuilder b, double[] array) { + if (array.length == 0) { + b.append("[]"); + } else { + double first = array[0]; + b.append("["); + json(b, first); + for (int i = 1; i < array.length; i++) { + b.append(","); + json(b, array[i]); + } + b.append("]"); + } + } + + private static void array(StringBuilder b, char[] array) { + if (array.length == 0) { + b.append("[]"); + } else { + char first = array[0]; + b.append("["); + json(b, first); + for (int i = 1; i < array.length; i++) { + b.append(","); + json(b, array[i]); + } + b.append("]"); + } + } + + private static void array(StringBuilder b, float[] array) { + if (array.length == 0) { + b.append("[]"); + } else { + float first = array[0]; + b.append("["); + json(b, first); + for (int i = 1; i < array.length; i++) { + b.append(","); + json(b, array[i]); + } + b.append("]"); + } + } + + private static void array(StringBuilder b, int[] array) { + if (array.length == 0) { + b.append("[]"); + } else { + int first = array[0]; + b.append("["); + json(b, first); + for (int i = 1; i < array.length; i++) { + b.append(","); + json(b, array[i]); + } + b.append("]"); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static void list(StringBuilder b, Collection list) { + if (list.isEmpty()) { + b.append("[]"); + } else { + Object first = list.iterator().next(); + b.append("["); + Mapper.json(b, first); + list.stream().skip(1) + .forEach(element -> { + b.append(","); + Mapper.json(b, element); + }); + b.append("]"); + } + } + + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static void object(StringBuilder b, Map map) { + if (map.isEmpty()) { + b.append("{}"); + } else { + b.append("{\""); + Set entries = map.entrySet(); + Map.Entry first = entries.iterator().next(); + Object key = first.getKey(); + if (key instanceof String) { + escape(b, (String) key); + } else if (key instanceof Character) { + escape(b, (Character) key); + } else { + b.append(key); + } + b.append("\":"); + Mapper.json(b, first.getValue()); + entries.stream().skip(1) + .forEach(entry -> { + b.append(",\""); + b.append(entry.getKey()).append("\":"); + Mapper.json(b, entry.getValue()); + }); + b.append("}"); + } + } + + public static void json(StringBuilder b, byte value) { + b.append(value); + } + + public static void json(StringBuilder b, boolean value) { + b.append(value); + } + + public static void json(StringBuilder b, short value) { + b.append(value); + } + + public static void json(StringBuilder b, int value) { + b.append(value); + } + + public static void json(StringBuilder b, long value) { + b.append(value); + } + + public static void json(StringBuilder b, char value) { + b.append("\""); + escape(b, value); + b.append("\""); + } + + public static void json(StringBuilder b, float value) { + b.append(value); + } + + public static void json(StringBuilder b, double value) { + b.append(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); + } + } + + static void escape(StringBuilder b, char c) { + escape(b, String.valueOf(c)); + } + + static void escape(StringBuilder b, String value) { + int offset = b.length(); + b.append(value); + int i = offset; + 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++; + } + } +} + +class BooleanMapper extends BaseMapper { + + @Override + public void json(StringBuilder b, Boolean value) { + b.append(value); + } +} + +class ShortMapper extends BaseMapper { + + @Override + public void json(StringBuilder b, Short value) { + b.append(value); + } +} + +class StringMapper extends BaseMapper { + @Override + public void json(StringBuilder b, String value) { + b.append("\""); + Mapper.escape(b, value); + b.append("\""); + } +} + +class IntegerMapper extends BaseMapper { + + @Override + public void json(StringBuilder b, Integer value) { + b.append(value); + } +} + +class LongMapper extends BaseMapper { + + @Override + public void json(StringBuilder b, Long value) { + b.append(value); + } +} + +class ByteMapper extends BaseMapper { + + @Override + protected void json(StringBuilder b, Byte value) { + b.append(value); + } +} + +class CharMapper extends BaseMapper { + + @Override + protected void json(StringBuilder b, Character value) { + b.append("\""); + Mapper.escape(b, value); + b.append("\""); + } +} + +class FloatMapper extends BaseMapper { + + @Override + protected void json(StringBuilder b, Float value) { + b.append(value); + } +} + +class DoubleMapper extends BaseMapper { + + @Override + protected void json(StringBuilder b, Double value) { + b.append(value); + } +} \ No newline at end of file diff --git a/src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java b/lib/src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java similarity index 81% rename from src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java rename to lib/src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java index 0b50136..f14655a 100644 --- a/src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java +++ b/lib/src/main/java/nl/sanderhautvast/json/ser/MapperFactory.java @@ -45,7 +45,7 @@ public class MapperFactory extends ClassVisitor { @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.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)); @@ -53,17 +53,13 @@ public class MapperFactory extends ClassVisitor { constructor.instructions.add(new InsnNode(RETURN)); classNode.methods.add(constructor); - jsonMethod = new MethodNode(ACC_PUBLIC, - "json", "(Ljava/lang/Object;)Ljava/lang/String;", null, null); + jsonMethod = new MethodNode(ACC_PROTECTED, + "json", "(Ljava/lang/StringBuilder;Ljava/lang/Object;)V", 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 TypeInsnNode(CHECKCAST, name)); + add(new VarInsnNode(ASTORE, 3)); + add(new VarInsnNode(ALOAD, 1)); add(new LdcInsnNode("{")); add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE)); add(new InsnNode(POP)); @@ -86,8 +82,8 @@ public class MapperFactory extends ClassVisitor { startIndex = 3; } - if (!firstGetter){ - getterInsnList.add(new VarInsnNode(ALOAD, 2)); + if (!firstGetter) { + getterInsnList.add(new VarInsnNode(ALOAD, 1)); getterInsnList.add(new LdcInsnNode(",")); getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE)); getterInsnList.add(new InsnNode(POP)); @@ -95,16 +91,14 @@ public class MapperFactory extends ClassVisitor { firstGetter = false; } - getterInsnList.add(new VarInsnNode(ALOAD, 2)); + getterInsnList.add(new VarInsnNode(ALOAD, 1)); 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 VarInsnNode(ALOAD, 3)); 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)); + getterInsnList.add(new MethodInsnNode(INVOKESTATIC, MAPPER, "json", "(Ljava/lang/StringBuilder;" + genericReturnType(returnType) + ")V")); } private static String genericReturnType(String returnType) { @@ -121,12 +115,10 @@ public class MapperFactory extends ClassVisitor { for (AbstractInsnNode insn : getterInsnList) { add(insn); } - add(new VarInsnNode(ALOAD, 2)); + add(new VarInsnNode(ALOAD, 1)); 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)); + add(new InsnNode(RETURN)); } private void add(AbstractInsnNode ins) { diff --git a/src/test/java/nl/sanderhautvast/json/ser/ByteClassLoaderTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/ByteClassLoaderTest.java similarity index 100% rename from src/test/java/nl/sanderhautvast/json/ser/ByteClassLoaderTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/ByteClassLoaderTest.java diff --git a/lib/src/test/java/nl/sanderhautvast/json/ser/ExampleMapper.java b/lib/src/test/java/nl/sanderhautvast/json/ser/ExampleMapper.java new file mode 100644 index 0000000..6865566 --- /dev/null +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/ExampleMapper.java @@ -0,0 +1,16 @@ +package nl.sanderhautvast.json.ser; + + +import nl.sanderhautvast.json.ser.nested.Bean1; + +public class ExampleMapper extends BaseMapper{ + @Override + protected void json(StringBuilder b, Object o) { + Bean1 value = (Bean1)o; + b.append("{"); + b.append("data1"); + b.append(":"); + Mapper.json(b, value.getData1()); + b.append("}"); + } +} diff --git a/src/test/java/nl/sanderhautvast/json/ser/performance/JacksonComparisonTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/JacksonComparisonTest.java similarity index 94% rename from src/test/java/nl/sanderhautvast/json/ser/performance/JacksonComparisonTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/JacksonComparisonTest.java index 46e9364..a73e3fd 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/performance/JacksonComparisonTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/JacksonComparisonTest.java @@ -1,8 +1,7 @@ -package nl.sanderhautvast.json.ser.performance; +package nl.sanderhautvast.json.ser; 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; @@ -10,7 +9,7 @@ import org.junit.jupiter.api.Test; import java.util.*; /* - * => about 10% faster than jackson + * not >real< benchmark. quickly evaluate changes */ public class JacksonComparisonTest { private static final int ITERATIONS = 20; diff --git a/src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java similarity index 71% rename from src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java index eca42db..68e0967 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/StringPropertyTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser; -import lombok.Data; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -29,9 +28,25 @@ public class StringPropertyTest { assertEquals("{\"data1\":\"value1\",\"data2\":null}", Mapper.json(object)); } - @Data + @SuppressWarnings("unused") public static 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/sanderhautvast/json/ser/collections/ArrayTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/ArrayTest.java similarity index 68% rename from src/test/java/nl/sanderhautvast/json/ser/collections/ArrayTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/collections/ArrayTest.java index 3172529..7372c89 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/collections/ArrayTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/ArrayTest.java @@ -1,7 +1,6 @@ package nl.sanderhautvast.json.ser.collections; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -13,6 +12,11 @@ public class ArrayTest { assertEquals("[\"value1\",\"value2\"]", Mapper.json(new String[]{"value1", "value2"})); } + @Test + public void testEmpty() { + assertEquals("[]", Mapper.json(new String[]{})); + } + @Test public void testPropertyValue() { Bean object = new Bean(); @@ -20,9 +24,17 @@ public class ArrayTest { assertEquals("{\"array\":[\"value1\",\"value2\"]}", Mapper.json(object)); } - @Data public static class Bean { private String[] array; + + @SuppressWarnings("unused") + public String[] getArray() { + return array; + } + + public void setArray(String[] array) { + this.array = array; + } } } diff --git a/src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java similarity index 81% rename from src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java index a49b36d..bf082f1 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/ListTest.java @@ -1,7 +1,6 @@ package nl.sanderhautvast.json.ser.collections; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -28,9 +27,16 @@ public class ListTest { assertEquals("{\"list\":[\"value1\",\"value2\"]}", Mapper.json((object))); } - @Data public static class Bean { private List list; + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } } } diff --git a/src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java similarity index 87% rename from src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java index 2379c49..750e589 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/MapTest.java @@ -1,7 +1,6 @@ package nl.sanderhautvast.json.ser.collections; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -40,8 +39,16 @@ public class MapTest { || "{\"map\":{\"key2\":\"value2\",\"key1\":\"value1\"}}".equals(jsonString), jsonString); } - @Data public static class Bean { private Map map; + + @SuppressWarnings("unused") + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } } } diff --git a/src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java similarity index 86% rename from src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java index cab57d8..314f2b2 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/SetTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.collections; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -31,8 +30,15 @@ public class SetTest { || "{\"set\":[\"value1\",\"value2\"]}".equals(jsonString)); } - @Data public static 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/sanderhautvast/json/ser/collections/StringTests.java b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/StringTests.java similarity index 92% rename from src/test/java/nl/sanderhautvast/json/ser/collections/StringTests.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/collections/StringTests.java index f8bec04..41d7706 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/collections/StringTests.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/collections/StringTests.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.collections; -import com.fasterxml.jackson.core.JsonProcessingException; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -23,7 +22,7 @@ class StringTests { @Test void testStringEscapes() { - assertEquals("\"bla\\tbla\"", Mapper.json("bla\tbla")); +// 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")); diff --git a/lib/src/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java b/lib/src/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java new file mode 100644 index 0000000..7d0efe0 --- /dev/null +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java @@ -0,0 +1,24 @@ +package nl.sanderhautvast.json.ser.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; + } +} diff --git a/lib/src/test/java/nl/sanderhautvast/json/ser/nested/Bean2.java b/lib/src/test/java/nl/sanderhautvast/json/ser/nested/Bean2.java new file mode 100644 index 0000000..48e3118 --- /dev/null +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/nested/Bean2.java @@ -0,0 +1,15 @@ +package nl.sanderhautvast.json.ser.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/sanderhautvast/json/ser/nested/NestedBeanTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/nested/NestedBeanTest.java similarity index 100% rename from src/test/java/nl/sanderhautvast/json/ser/nested/NestedBeanTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/nested/NestedBeanTest.java diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java similarity index 81% rename from src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java index cf35eee..1cf99f4 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/BooleanPropertyTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.primitives; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -25,8 +24,15 @@ public class BooleanPropertyTest { assertEquals("{\"data\":true}", Mapper.json(object)); } - @Data public static class Bean { boolean data; + + public boolean isData() { + return data; + } + + public void setData(boolean data) { + this.data = data; + } } } diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java similarity index 81% rename from src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java index acc4176..8c6c6d6 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/BytePropertyTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.primitives; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -24,8 +23,15 @@ public class BytePropertyTest { assertEquals("{\"data\":1}", Mapper.json(object)); } - @Data public static 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/sanderhautvast/json/ser/primitives/CharPropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/CharPropertyTest.java similarity index 81% rename from src/test/java/nl/sanderhautvast/json/ser/primitives/CharPropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/primitives/CharPropertyTest.java index e3b6164..fae2f4d 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/primitives/CharPropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/CharPropertyTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.primitives; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -24,8 +23,15 @@ public class CharPropertyTest { assertEquals("{\"data\":\"a\"}", Mapper.json(object)); } - @Data public static 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/sanderhautvast/json/ser/primitives/DoublePropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/DoublePropertyTest.java similarity index 78% rename from src/test/java/nl/sanderhautvast/json/ser/primitives/DoublePropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/primitives/DoublePropertyTest.java index 891ef72..cfd6cc6 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/primitives/DoublePropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/DoublePropertyTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.primitives; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -24,8 +23,16 @@ public class DoublePropertyTest { assertEquals("{\"data\":326.2}", Mapper.json(object)); } - @Data public static class Bean { private double data; + + @SuppressWarnings("unused") + public double getData() { + return data; + } + + public void setData(double data) { + this.data = data; + } } } diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java similarity index 81% rename from src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java index d563746..88f79bc 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/FloatPropertyTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.primitives; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -24,8 +23,15 @@ public class FloatPropertyTest { assertEquals("{\"data\":1.0}", Mapper.json(object)); } - @Data public static 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/sanderhautvast/json/ser/primitives/IntPropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/IntPropertyTest.java similarity index 81% rename from src/test/java/nl/sanderhautvast/json/ser/primitives/IntPropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/primitives/IntPropertyTest.java index a692b6f..9c38bcd 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/primitives/IntPropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/IntPropertyTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.primitives; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -25,8 +24,15 @@ public class IntPropertyTest { assertEquals("{\"data\":1}", Mapper.json(object)); } - @Data public static class Bean { int data; + + public int getData() { + return data; + } + + public void setData(int data) { + this.data = data; + } } } diff --git a/src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java similarity index 81% rename from src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java index bb35e85..a265cdf 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/LongPropertyTest.java @@ -1,12 +1,10 @@ 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() { @@ -25,8 +23,15 @@ public class LongPropertyTest { assertEquals("{\"data\":1}", Mapper.json(object)); } - @Data public static 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/sanderhautvast/json/ser/primitives/NullPropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/NullPropertyTest.java similarity index 73% rename from src/test/java/nl/sanderhautvast/json/ser/primitives/NullPropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/primitives/NullPropertyTest.java index f7a9cdf..637c1c0 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/primitives/NullPropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/NullPropertyTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.primitives; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -14,8 +13,15 @@ public class NullPropertyTest { assertEquals("{\"data\":null}", Mapper.json(object)); } - @Data public static 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/sanderhautvast/json/ser/primitives/ShortPropertyTest.java b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/ShortPropertyTest.java similarity index 78% rename from src/test/java/nl/sanderhautvast/json/ser/primitives/ShortPropertyTest.java rename to lib/src/test/java/nl/sanderhautvast/json/ser/primitives/ShortPropertyTest.java index 1aef1a6..fcdd29e 100644 --- a/src/test/java/nl/sanderhautvast/json/ser/primitives/ShortPropertyTest.java +++ b/lib/src/test/java/nl/sanderhautvast/json/ser/primitives/ShortPropertyTest.java @@ -1,6 +1,5 @@ package nl.sanderhautvast.json.ser.primitives; -import lombok.Data; import nl.sanderhautvast.json.ser.Mapper; import org.junit.jupiter.api.Test; @@ -24,8 +23,15 @@ public class ShortPropertyTest { assertEquals("{\"data\":3}", Mapper.json(object)); } - @Data public static class Bean { private short data; + @SuppressWarnings("unused") + public short getData() { + return data; + } + + public void setData(short data) { + this.data = data; + } } } diff --git a/pom.xml b/pom.xml index 6b48d65..2a5794d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,9 +4,9 @@ JsonToy nl.sander - jsonthingy + jsonthingy-pom 0.1-SNAPSHOT - jar + pom 9 @@ -14,50 +14,52 @@ UTF-8 - - - org.ow2.asm - asm-tree - 9.4 - - - org.junit.jupiter - junit-jupiter - 5.9.3 - test - - - - org.mockito - mockito-all - 1.10.19 - test - - - org.projectlombok - lombok - 1.18.26 - test - - - - com.fasterxml.jackson.core - jackson-databind - 2.15.1 - test - - + + lib + jmh + - - - org.apache.maven.plugins - maven-surefire-plugin - 3.1.0 - - --add-opens java.base/java.lang=ALL-UNNAMED - - - + + + + maven-clean-plugin + 2.5 + + + maven-deploy-plugin + 2.8.1 + + + maven-install-plugin + 2.5.1 + + + maven-jar-plugin + 2.4 + + + maven-javadoc-plugin + 2.9.1 + + + maven-resources-plugin + 2.6 + + + maven-site-plugin + 3.3 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.17 + + + + diff --git a/src/main/java/nl/sanderhautvast/json/ser/Mapper.java b/src/main/java/nl/sanderhautvast/json/ser/Mapper.java deleted file mode 100644 index fca03e6..0000000 --- a/src/main/java/nl/sanderhautvast/json/ser/Mapper.java +++ /dev/null @@ -1,302 +0,0 @@ -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/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java b/src/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java deleted file mode 100644 index 3db19de..0000000 --- a/src/test/java/nl/sanderhautvast/json/ser/nested/Bean1.java +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index de08b97..0000000 --- a/src/test/java/nl/sanderhautvast/json/ser/nested/Bean2.java +++ /dev/null @@ -1,9 +0,0 @@ -package nl.sanderhautvast.json.ser.nested; - - -import lombok.Data; - -@Data -public class Bean2 { - private String data2; -}