changed everything

This commit is contained in:
Shautvast 2023-06-20 18:13:30 +02:00
parent 9df9fba621
commit f4d9d0c8c5
49 changed files with 1078 additions and 1123 deletions

View file

@ -1,9 +1,11 @@
# JsonToy # Json
a JSON serializer based on bytecode manipulation a JSON serializer based on bytecode manipulation
* creates a Json serializer for a java type using javassist * uses ASM for creating object mappers
* deserializing not yet implemented * deserializing not (yet?) implemented
* see the unit tests to see how it works * write to String: Mapper.json(...)
* as of java9 it needs `--add-opens java.base/java.lang=ALL-UNNAMED` as java commandline option. * ~~as of java9 it needs `--add-opens java.base/java.lang=ALL-UNNAMED` as java commandline option.~~
* prerequisite: at least jdk9

View file

@ -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;
}
}

View file

@ -1,21 +0,0 @@
package nl.jssl.jsontoy.serialize;
import java.util.Formatter;
public abstract class JSONSerializer<T> {
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);
}
}
}

View file

@ -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 <T> String toJSONString(T o) {
if (o == null) {
return "null";
}
return instance.createSerializer((Class<T>) o.getClass()).toJSONString(o);
}
}

View file

@ -1,10 +0,0 @@
package nl.jssl.jsontoy.serialize;
@SuppressWarnings("serial")
public class SerializerCreationException extends RuntimeException {
public SerializerCreationException(Throwable t) {
super(t);
}
}

View file

@ -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<String> 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<Class<?>, 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")
<T> JSONSerializer<T> createSerializer(Class<T> beanjavaClass) {
JSONSerializer<T> serializer = (JSONSerializer<T>) 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 <T> JSONSerializer<T> 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<T> 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<CtMethod> 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<array.length; i++){\n";
source += "\t\tresult.append(" + Serializer.class.getName() + ".toJSONString(array[i]));\n";
source += "\t\tresult.append(\", \");\n";
source += "\t};\n";
source += "\tresult.setLength(result.length()-2);\n";
source += "\tresult.append(\"]\");\n";
source += "\treturn result.toString();\n";
source += "}";
return source;
}
private String handleMap() {
String source = "StringBuilder result=new StringBuilder(\"{\");\n";
source += "\tfor (java.util.Iterator entries=((java.util.Map)object).entrySet().iterator();entries.hasNext();){\n";
source += "\t\tjava.util.Map.Entry entry=(java.util.Map.Entry)entries.next();\n";
source += "\t\tresult.append(\"\\\"\"+entry.getKey().toString()+\"\\\"\");\n";
source += "\t\tresult.append(\": \");\n";
source += "\t\tresult.append(" + Serializer.class.getName() + ".toJSONString(entry.getValue()));\n";
source += "\t\tresult.append(\", \");\n";
source += "\t};\n";
source += "\tresult.setLength(result.length()-2);\n";
source += "\tresult.append(\"}\");\n";
source += "\treturn result.toString();\n";
source += "}";
return source;
}
/*
* If the class contains fields for which public getters are available, then these will be called in the generated code.
*/
private String addGetterCallers(CtClass beanClass, String source, List<CtMethod> 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 <T> JSONSerializer<T> createSerializerInstance(CtClass serializerClass) throws InstantiationException, IllegalAccessException,
CannotCompileException, NoSuchMethodException, InvocationTargetException {
return (JSONSerializer<T>) 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<CtClass> 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<CtClass> 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<CtMethod> getGetters(CtClass beanClass) {
List<CtMethod> methods = new ArrayList<>();
List<CtField> 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<CtField> getAllFields(CtClass beanClass) {
try {
List<CtField> 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<CtField> getAllFields(CtClass beanClass, List<CtField> allfields) {
allfields.addAll(Arrays.asList(beanClass.getDeclaredFields()));
return allfields;
}
/*
* is getter list is not empty then callers should be added
*/
boolean shouldAddGetterCallers(List<CtMethod> 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());
}
}

View file

@ -0,0 +1,7 @@
package nl.sanderhautvast.json.ser;
public abstract class BaseMapper<T> {
protected abstract String json(T value);
}

View file

@ -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<String, Class<?>> 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);
}
}

View file

@ -0,0 +1,7 @@
package nl.sanderhautvast.json.ser;
public class JsonError extends RuntimeException{
public JsonError(Throwable cause) {
super(cause);
}
}

View file

@ -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<Class<?>, 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 <T> void addMapper(Class<T> type, BaseMapper<T> mapper) {
mappers.put(type, mapper);
}
/**
* returns the json representation of the value as a String
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static <J> String json(Object value) {
if (value == null) {
return "null";
}
Class<J> type = (Class<J>) 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<Map.Entry> 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 <T> BaseMapper<T> createObjectMapper(Class<T> 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<T>) 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<Boolean> {
@Override
public String json(Boolean b) {
return Boolean.toString(b);
}
}
class ShortMapper extends BaseMapper<Short> {
@Override
public String json(Short value) {
return Short.toString(value);
}
}
class StringMapper extends BaseMapper<String> {
@Override
public String json(String value) {
return "\"" + Mapper.escape(value) + "\"";
}
}
class IntegerMapper extends BaseMapper<Integer> {
@Override
public String json(Integer value) {
return Integer.toString(value);
}
}
class LongMapper extends BaseMapper<Long> {
@Override
public String json(Long value) {
return Long.toString(value);
}
}
class ByteMapper extends BaseMapper<Byte> {
@Override
protected String json(Byte value) {
return Byte.toString(value);
}
}
class CharMapper extends BaseMapper<Character> {
@Override
protected String json(Character value) {
return "\"" + value + "\"";
}
}
class FloatMapper extends BaseMapper<Float> {
@Override
protected String json(Float value) {
return Float.toString(value);
}
}
class DoubleMapper extends BaseMapper<Double> {
@Override
protected String json(Double value) {
return Double.toString(value);
}
}

View file

@ -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 = "<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<AbstractInsnNode> 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, "<init>", "()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("\\.", "/");
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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<String> 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;
}
}
}

View file

@ -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<String, String> 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<String, String> 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<String, String> map;
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
}

View file

@ -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<String> list = new HashSet<String>();
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<String>(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<String> set;
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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));
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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"));
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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<String> list;
}
}

View file

@ -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<String, String> map;
}
}

View file

@ -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<String> set;
}
}

View file

@ -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
}
}

View file

@ -0,0 +1,10 @@
package nl.sanderhautvast.json.ser.nested;
import lombok.Data;
@Data
public class Bean1 {
private String data1;
private Bean2 bean2;
}

View file

@ -0,0 +1,9 @@
package nl.sanderhautvast.json.ser.nested;
import lombok.Data;
@Data
public class Bean2 {
private String data2;
}

View file

@ -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));
// }
//}

View file

@ -1,22 +1,21 @@
package nl.jssl.jsontoy.serialize.performance; package nl.sanderhautvast.json.ser.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;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; 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 { public class JacksonComparisonTest {
private static final int ITERATIONS = 20; private static final int ITERATIONS = 20;
private static final int INNERLOOP_COUNT = 100000; private static final int INNERLOOP_COUNT = 100000;
List<String> trashbin = new ArrayList<>(); private final List<String> trashbin = new ArrayList<>();
@Test @Test
public void testPerformance() throws JsonProcessingException { public void testPerformance() throws JsonProcessingException {
@ -52,7 +51,7 @@ public class JacksonComparisonTest {
bean1.setData1(UUID.randomUUID().toString()); bean1.setData1(UUID.randomUUID().toString());
bean1.setBean2(bean2); bean1.setBean2(bean2);
bean2.setData2(UUID.randomUUID().toString()); bean2.setData2(UUID.randomUUID().toString());
jsonString = Serializer.toJSONString(bean1); jsonString = Mapper.json(bean1);
trashbin.add(jsonString); trashbin.add(jsonString);
} }
System.out.printf("% 7d%n",System.currentTimeMillis() - tt0); System.out.printf("% 7d%n",System.currentTimeMillis() - tt0);

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}