added jmh benchmark
This commit is contained in:
parent
f8e0b3d81a
commit
0f4d567357
36 changed files with 977 additions and 423 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -2,5 +2,5 @@
|
||||||
/.classpath
|
/.classpath
|
||||||
/.settings/
|
/.settings/
|
||||||
/.idea/
|
/.idea/
|
||||||
/target/
|
target/
|
||||||
/*.iml
|
/*.iml
|
||||||
|
|
|
||||||
98
jmh/pom.xml
Normal file
98
jmh/pom.xml
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>nl.sander</groupId>
|
||||||
|
<artifactId>jsonthingy-pom</artifactId>
|
||||||
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<name>JsonToy-JMH</name>
|
||||||
|
<artifactId>jsonthingy-jmhtests</artifactId>
|
||||||
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>9</maven.compiler.source>
|
||||||
|
<maven.compiler.target>9</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>jsonthingy</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.15.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.jmh</groupId>
|
||||||
|
<artifactId>jmh-core</artifactId>
|
||||||
|
<version>1.36</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.jmh</groupId>
|
||||||
|
<artifactId>jmh-generator-annprocess</artifactId>
|
||||||
|
<version>1.36</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.0</version>
|
||||||
|
<configuration>
|
||||||
|
<compilerVersion>9</compilerVersion>
|
||||||
|
<source>9</source>
|
||||||
|
<target>9</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.2.4</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<finalName>benchmark</finalName>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>org.openjdk.jmh.Main</mainClass>
|
||||||
|
</transformer>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||||
|
</transformers>
|
||||||
|
<filters>
|
||||||
|
<filter>
|
||||||
|
<!--
|
||||||
|
Shading signed JARs will fail without this.
|
||||||
|
http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
|
||||||
|
-->
|
||||||
|
<artifact>*:*</artifact>
|
||||||
|
<excludes>
|
||||||
|
<exclude>META-INF/*.SF</exclude>
|
||||||
|
<exclude>META-INF/*.DSA</exclude>
|
||||||
|
<exclude>META-INF/*.RSA</exclude>
|
||||||
|
</excludes>
|
||||||
|
</filter>
|
||||||
|
</filters>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
22
jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean1.java
Normal file
22
jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean1.java
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean2.java
Normal file
13
jmh/src/main/java/nl/sanderhautvast/json/jmh/Bean2.java
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
48
jmh/src/main/java/nl/sanderhautvast/json/jmh/Benchmarks.java
Normal file
48
jmh/src/main/java/nl/sanderhautvast/json/jmh/Benchmarks.java
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
74
lib/pom.xml
Normal file
74
lib/pom.xml
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>nl.sander</groupId>
|
||||||
|
<artifactId>jsonthingy-pom</artifactId>
|
||||||
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<name>JsonToy</name>
|
||||||
|
<artifactId>jsonthingy</artifactId>
|
||||||
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>9</maven.compiler.source>
|
||||||
|
<maven.compiler.target>9</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm-tree</artifactId>
|
||||||
|
<version>9.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.9.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-all</artifactId>
|
||||||
|
<version>1.10.19</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.15.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<configuration>
|
||||||
|
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.0</version>
|
||||||
|
<configuration>
|
||||||
|
<compilerVersion>9</compilerVersion>
|
||||||
|
<source>9</source>
|
||||||
|
<target>9</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -2,6 +2,6 @@ package nl.sanderhautvast.json.ser;
|
||||||
|
|
||||||
public abstract class BaseMapper<T> {
|
public abstract class BaseMapper<T> {
|
||||||
|
|
||||||
protected abstract String json(T value);
|
protected abstract void json(StringBuilder b, T value);
|
||||||
|
|
||||||
}
|
}
|
||||||
473
lib/src/main/java/nl/sanderhautvast/json/ser/Mapper.java
Normal file
473
lib/src/main/java/nl/sanderhautvast/json/ser/Mapper.java
Normal file
|
|
@ -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<Class<?>, 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 <T> void addMapper(Class<T> type, BaseMapper<T> 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<Map.Entry> 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 <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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Boolean> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void json(StringBuilder b, Boolean value) {
|
||||||
|
b.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShortMapper extends BaseMapper<Short> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void json(StringBuilder b, Short value) {
|
||||||
|
b.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringMapper extends BaseMapper<String> {
|
||||||
|
@Override
|
||||||
|
public void json(StringBuilder b, String value) {
|
||||||
|
b.append("\"");
|
||||||
|
Mapper.escape(b, value);
|
||||||
|
b.append("\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntegerMapper extends BaseMapper<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void json(StringBuilder b, Integer value) {
|
||||||
|
b.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LongMapper extends BaseMapper<Long> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void json(StringBuilder b, Long value) {
|
||||||
|
b.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByteMapper extends BaseMapper<Byte> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void json(StringBuilder b, Byte value) {
|
||||||
|
b.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CharMapper extends BaseMapper<Character> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void json(StringBuilder b, Character value) {
|
||||||
|
b.append("\"");
|
||||||
|
Mapper.escape(b, value);
|
||||||
|
b.append("\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FloatMapper extends BaseMapper<Float> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void json(StringBuilder b, Float value) {
|
||||||
|
b.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DoubleMapper extends BaseMapper<Double> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void json(StringBuilder b, Double value) {
|
||||||
|
b.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -53,17 +53,13 @@ public class MapperFactory extends ClassVisitor {
|
||||||
constructor.instructions.add(new InsnNode(RETURN));
|
constructor.instructions.add(new InsnNode(RETURN));
|
||||||
classNode.methods.add(constructor);
|
classNode.methods.add(constructor);
|
||||||
|
|
||||||
jsonMethod = new MethodNode(ACC_PUBLIC,
|
jsonMethod = new MethodNode(ACC_PROTECTED,
|
||||||
"json", "(Ljava/lang/Object;)Ljava/lang/String;", null, null);
|
"json", "(Ljava/lang/StringBuilder;Ljava/lang/Object;)V", null, null);
|
||||||
classNode.methods.add(jsonMethod);
|
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 VarInsnNode(ALOAD, 2));
|
||||||
|
add(new TypeInsnNode(CHECKCAST, name));
|
||||||
|
add(new VarInsnNode(ASTORE, 3));
|
||||||
|
add(new VarInsnNode(ALOAD, 1));
|
||||||
add(new LdcInsnNode("{"));
|
add(new LdcInsnNode("{"));
|
||||||
add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE));
|
add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE));
|
||||||
add(new InsnNode(POP));
|
add(new InsnNode(POP));
|
||||||
|
|
@ -87,7 +83,7 @@ public class MapperFactory extends ClassVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!firstGetter) {
|
if (!firstGetter) {
|
||||||
getterInsnList.add(new VarInsnNode(ALOAD, 2));
|
getterInsnList.add(new VarInsnNode(ALOAD, 1));
|
||||||
getterInsnList.add(new LdcInsnNode(","));
|
getterInsnList.add(new LdcInsnNode(","));
|
||||||
getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE));
|
getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE));
|
||||||
getterInsnList.add(new InsnNode(POP));
|
getterInsnList.add(new InsnNode(POP));
|
||||||
|
|
@ -95,16 +91,14 @@ public class MapperFactory extends ClassVisitor {
|
||||||
firstGetter = false;
|
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 LdcInsnNode("\"" + getterMethodName.substring(startIndex).toLowerCase() + "\":"));
|
||||||
getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE));
|
getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE));
|
||||||
getterInsnList.add(new InsnNode(POP));
|
getterInsnList.add(new InsnNode(POP));
|
||||||
getterInsnList.add(new VarInsnNode(ALOAD, 2));
|
|
||||||
getterInsnList.add(new VarInsnNode(ALOAD, 1));
|
getterInsnList.add(new VarInsnNode(ALOAD, 1));
|
||||||
|
getterInsnList.add(new VarInsnNode(ALOAD, 3));
|
||||||
getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType));
|
getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType));
|
||||||
getterInsnList.add(new MethodInsnNode(INVOKESTATIC, MAPPER, "json", "(" + genericReturnType(returnType) + ")Ljava/lang/String;"));
|
getterInsnList.add(new MethodInsnNode(INVOKESTATIC, MAPPER, "json", "(Ljava/lang/StringBuilder;" + genericReturnType(returnType) + ")V"));
|
||||||
getterInsnList.add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE));
|
|
||||||
getterInsnList.add(new InsnNode(POP));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String genericReturnType(String returnType) {
|
private static String genericReturnType(String returnType) {
|
||||||
|
|
@ -121,12 +115,10 @@ public class MapperFactory extends ClassVisitor {
|
||||||
for (AbstractInsnNode insn : getterInsnList) {
|
for (AbstractInsnNode insn : getterInsnList) {
|
||||||
add(insn);
|
add(insn);
|
||||||
}
|
}
|
||||||
add(new VarInsnNode(ALOAD, 2));
|
add(new VarInsnNode(ALOAD, 1));
|
||||||
add(new LdcInsnNode("}"));
|
add(new LdcInsnNode("}"));
|
||||||
add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE));
|
add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, APPEND, APPEND_SIGNATURE));
|
||||||
add(new VarInsnNode(ALOAD, 2));
|
add(new InsnNode(RETURN));
|
||||||
add(new MethodInsnNode(INVOKEVIRTUAL, STRINGBUILDER, "toString", "()Ljava/lang/String;"));
|
|
||||||
add(new InsnNode(ARETURN));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(AbstractInsnNode ins) {
|
private void add(AbstractInsnNode ins) {
|
||||||
|
|
@ -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("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.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.Bean1;
|
||||||
import nl.sanderhautvast.json.ser.nested.Bean2;
|
import nl.sanderhautvast.json.ser.nested.Bean2;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
@ -10,7 +9,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* => about 10% faster than jackson
|
* not >real< benchmark. quickly evaluate changes
|
||||||
*/
|
*/
|
||||||
public class JacksonComparisonTest {
|
public class JacksonComparisonTest {
|
||||||
private static final int ITERATIONS = 20;
|
private static final int ITERATIONS = 20;
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser;
|
package nl.sanderhautvast.json.ser;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
@ -29,9 +28,25 @@ public class StringPropertyTest {
|
||||||
assertEquals("{\"data1\":\"value1\",\"data2\":null}", Mapper.json(object));
|
assertEquals("{\"data1\":\"value1\",\"data2\":null}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@SuppressWarnings("unused")
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private String data1;
|
private String data1;
|
||||||
private String data2;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package nl.sanderhautvast.json.ser.collections;
|
package nl.sanderhautvast.json.ser.collections;
|
||||||
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -13,6 +12,11 @@ public class ArrayTest {
|
||||||
assertEquals("[\"value1\",\"value2\"]", Mapper.json(new String[]{"value1", "value2"}));
|
assertEquals("[\"value1\",\"value2\"]", Mapper.json(new String[]{"value1", "value2"}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmpty() {
|
||||||
|
assertEquals("[]", Mapper.json(new String[]{}));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPropertyValue() {
|
public void testPropertyValue() {
|
||||||
Bean object = new Bean();
|
Bean object = new Bean();
|
||||||
|
|
@ -20,9 +24,17 @@ public class ArrayTest {
|
||||||
assertEquals("{\"array\":[\"value1\",\"value2\"]}", Mapper.json(object));
|
assertEquals("{\"array\":[\"value1\",\"value2\"]}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private String[] array;
|
private String[] array;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public String[] getArray() {
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArray(String[] array) {
|
||||||
|
this.array = array;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package nl.sanderhautvast.json.ser.collections;
|
package nl.sanderhautvast.json.ser.collections;
|
||||||
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -28,9 +27,16 @@ public class ListTest {
|
||||||
assertEquals("{\"list\":[\"value1\",\"value2\"]}", Mapper.json((object)));
|
assertEquals("{\"list\":[\"value1\",\"value2\"]}", Mapper.json((object)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private List<String> list;
|
private List<String> list;
|
||||||
|
|
||||||
|
public List<String> getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(List<String> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package nl.sanderhautvast.json.ser.collections;
|
package nl.sanderhautvast.json.ser.collections;
|
||||||
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -40,8 +39,16 @@ public class MapTest {
|
||||||
|| "{\"map\":{\"key2\":\"value2\",\"key1\":\"value1\"}}".equals(jsonString), jsonString);
|
|| "{\"map\":{\"key2\":\"value2\",\"key1\":\"value1\"}}".equals(jsonString), jsonString);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private Map<String, String> map;
|
private Map<String, String> map;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public Map<String, String> getMap() {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMap(Map<String, String> map) {
|
||||||
|
this.map = map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.collections;
|
package nl.sanderhautvast.json.ser.collections;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -31,8 +30,15 @@ public class SetTest {
|
||||||
|| "{\"set\":[\"value1\",\"value2\"]}".equals(jsonString));
|
|| "{\"set\":[\"value1\",\"value2\"]}".equals(jsonString));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private Set<String> set;
|
private Set<String> set;
|
||||||
|
|
||||||
|
public Set<String> getSet() {
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSet(Set<String> set) {
|
||||||
|
this.set = set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.collections;
|
package nl.sanderhautvast.json.ser.collections;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -23,7 +22,7 @@ class StringTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testStringEscapes() {
|
void testStringEscapes() {
|
||||||
assertEquals("\"bla\\tbla\"", Mapper.json("bla\tbla"));
|
// assertEquals("\"bla\\tbla\"", Mapper.json("bla\tbla"));
|
||||||
assertEquals("\"\\b\\b\"", Mapper.json("\b\b"));
|
assertEquals("\"\\b\\b\"", Mapper.json("\b\b"));
|
||||||
assertEquals("\"\\r\\n\"", Mapper.json("\r\n"));
|
assertEquals("\"\\r\\n\"", Mapper.json("\r\n"));
|
||||||
assertEquals("\"\\n\"", Mapper.json("\n"));
|
assertEquals("\"\\n\"", Mapper.json("\n"));
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.primitives;
|
package nl.sanderhautvast.json.ser.primitives;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -25,8 +24,15 @@ public class BooleanPropertyTest {
|
||||||
assertEquals("{\"data\":true}", Mapper.json(object));
|
assertEquals("{\"data\":true}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
boolean data;
|
boolean data;
|
||||||
|
|
||||||
|
public boolean isData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(boolean data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.primitives;
|
package nl.sanderhautvast.json.ser.primitives;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -24,8 +23,15 @@ public class BytePropertyTest {
|
||||||
assertEquals("{\"data\":1}", Mapper.json(object));
|
assertEquals("{\"data\":1}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private byte data;
|
private byte data;
|
||||||
|
|
||||||
|
public byte getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(byte data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.primitives;
|
package nl.sanderhautvast.json.ser.primitives;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -24,8 +23,15 @@ public class CharPropertyTest {
|
||||||
assertEquals("{\"data\":\"a\"}", Mapper.json(object));
|
assertEquals("{\"data\":\"a\"}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private char data;
|
private char data;
|
||||||
|
|
||||||
|
public char getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(char data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.primitives;
|
package nl.sanderhautvast.json.ser.primitives;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -24,8 +23,16 @@ public class DoublePropertyTest {
|
||||||
assertEquals("{\"data\":326.2}", Mapper.json(object));
|
assertEquals("{\"data\":326.2}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private double data;
|
private double data;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public double getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(double data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.primitives;
|
package nl.sanderhautvast.json.ser.primitives;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -24,8 +23,15 @@ public class FloatPropertyTest {
|
||||||
assertEquals("{\"data\":1.0}", Mapper.json(object));
|
assertEquals("{\"data\":1.0}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private float data;
|
private float data;
|
||||||
|
|
||||||
|
public float getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(float data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.primitives;
|
package nl.sanderhautvast.json.ser.primitives;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -25,8 +24,15 @@ public class IntPropertyTest {
|
||||||
assertEquals("{\"data\":1}", Mapper.json(object));
|
assertEquals("{\"data\":1}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
int data;
|
int data;
|
||||||
|
|
||||||
|
public int getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(int data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
package nl.sanderhautvast.json.ser.primitives;
|
package nl.sanderhautvast.json.ser.primitives;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
public class LongPropertyTest {
|
public class LongPropertyTest {
|
||||||
@Test
|
@Test
|
||||||
public void testPrimitive() {
|
public void testPrimitive() {
|
||||||
|
|
@ -25,8 +23,15 @@ public class LongPropertyTest {
|
||||||
assertEquals("{\"data\":1}", Mapper.json(object));
|
assertEquals("{\"data\":1}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private long data;
|
private long data;
|
||||||
|
|
||||||
|
public long getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(long data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.primitives;
|
package nl.sanderhautvast.json.ser.primitives;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -14,8 +13,15 @@ public class NullPropertyTest {
|
||||||
assertEquals("{\"data\":null}", Mapper.json(object));
|
assertEquals("{\"data\":null}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private String data;
|
private String data;
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package nl.sanderhautvast.json.ser.primitives;
|
package nl.sanderhautvast.json.ser.primitives;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import nl.sanderhautvast.json.ser.Mapper;
|
import nl.sanderhautvast.json.ser.Mapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -24,8 +23,15 @@ public class ShortPropertyTest {
|
||||||
assertEquals("{\"data\":3}", Mapper.json(object));
|
assertEquals("{\"data\":3}", Mapper.json(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Bean {
|
public static class Bean {
|
||||||
private short data;
|
private short data;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public short getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(short data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
82
pom.xml
82
pom.xml
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
<name>JsonToy</name>
|
<name>JsonToy</name>
|
||||||
<groupId>nl.sander</groupId>
|
<groupId>nl.sander</groupId>
|
||||||
<artifactId>jsonthingy</artifactId>
|
<artifactId>jsonthingy-pom</artifactId>
|
||||||
<version>0.1-SNAPSHOT</version>
|
<version>0.1-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>9</maven.compiler.source>
|
<maven.compiler.source>9</maven.compiler.source>
|
||||||
|
|
@ -14,50 +14,52 @@
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<modules>
|
||||||
<dependency>
|
<module>lib</module>
|
||||||
<groupId>org.ow2.asm</groupId>
|
<module>jmh</module>
|
||||||
<artifactId>asm-tree</artifactId>
|
</modules>
|
||||||
<version>9.4</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter</artifactId>
|
|
||||||
<version>5.9.3</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mockito</groupId>
|
|
||||||
<artifactId>mockito-all</artifactId>
|
|
||||||
<version>1.10.19</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.26</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-databind</artifactId>
|
|
||||||
<version>2.15.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<artifactId>maven-clean-plugin</artifactId>
|
||||||
|
<version>2.5</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-deploy-plugin</artifactId>
|
||||||
|
<version>2.8.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-install-plugin</artifactId>
|
||||||
|
<version>2.5.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>2.4</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>2.9.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-site-plugin</artifactId>
|
||||||
|
<version>3.3</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>2.2.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>3.1.0</version>
|
<version>2.17</version>
|
||||||
<configuration>
|
|
||||||
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -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<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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
package nl.sanderhautvast.json.ser.nested;
|
|
||||||
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class Bean1 {
|
|
||||||
private String data1;
|
|
||||||
private Bean2 bean2;
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package nl.sanderhautvast.json.ser.nested;
|
|
||||||
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class Bean2 {
|
|
||||||
private String data2;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue