JSONSerialiser.java
Home
/
src /
main /
java /
br /
ufrgs /
inf /
prosoft /
jsonserialiser /
JSONSerialiser.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package br.ufrgs.inf.prosoft.jsonserialiser;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
*
* @author romulo
*/
public class JSONSerialiser {
private final List<Object> visited;
private static final boolean TRACER_SERIALISE_INTERNALS = System.getenv("TRACER_SERIALISE_INTERNALS") != null && System.getenv("TRACER_SERIALISE_INTERNALS").equals("true");
private static final String TRACER_IGNORED_PACKAGES = System.getenv("TRACER_IGNORED_PACKAGES") != null ? System.getenv("TRACER_IGNORED_PACKAGES") : "./ignored";
private static final String TRACER_WHITELIST = System.getenv("TRACER_WHITELIST") != null ? System.getenv("TRACER_WHITELIST") : "";
private static final String TRACER_LOG = System.getenv("TRACER_LOG");
private JSONSerialiser() {
this.visited = new ArrayList<>();
}
public static String serialise(Object bean) {
JSONSerialiser jsonSerialiser = new JSONSerialiser();
StringBuilder stringBuilder = jsonSerialiser.wrap(bean);
Utils.fixJson(stringBuilder);
return stringBuilder.toString();
}
private static class Utils {
private static void fixJson(StringBuilder stringBuilder) {
int index = 0;
while (true) {
if (index + 1 >= stringBuilder.length()) {
break;
}
char thisChar = stringBuilder.charAt(index);
char nextChar = stringBuilder.charAt(index + 1);
if (thisChar == ',') {
if (nextChar == ',') {
stringBuilder.deleteCharAt(index);
continue;
}
if (nextChar == ']' || nextChar == '}') {
stringBuilder.deleteCharAt(index);
}
} else if (thisChar == '[' || thisChar == '{') {
if (nextChar == ',') {
stringBuilder.deleteCharAt(index + 1);
continue;
}
}
index++;
}
}
private static List<Field> getAllFields(List<Field> fields, Class<?> type) {
for (Field field : type.getDeclaredFields()) {
if (fields.contains(field)) {
continue;
}
if (!field.getName().startsWith("ajc$tjp_")) {
fields.add(field);
}
}
if (type.getSuperclass() != null) {
getAllFields(fields, type.getSuperclass());
}
return fields;
}
}
private StringBuilder wrap(Object object) {
try {
if (object == null) {
return new StringBuilder().append("null");
}
if (object instanceof Boolean) {
return new StringBuilder().append("\"").append(String.valueOf(object)).append("\"");
}
if (object instanceof Character) {
return serialiseString(String.valueOf(object));
}
if (object instanceof CharSequence) {
CharSequence string = (CharSequence) object;
return serialiseString(string);
}
if (object instanceof Number) {
return new StringBuilder().append("\"").append(String.valueOf(object)).append("\"");
}
if (object instanceof Enum) {
return serialiseString(((Enum<?>) object).name());
}
if (object instanceof HashSet) {
HashSet<?> hashSet = (HashSet<?>) object;
return serialiseHashSet(hashSet);
}
if (object instanceof Collection) {
Collection<?> collection = (Collection<?>) object;
return serialiseCollection(collection);
}
if (object.getClass().isArray()) {
return serialiseArray(object);
}
if (object instanceof HashMap) {
HashMap<?, ?> hashMap = (HashMap<?, ?>) object;
return serialiseHashMap(hashMap);
}
if (object instanceof Map) {
Map<?, ?> map = (Map<?, ?>) object;
return serialiseMap(map);
}
if (object instanceof Date) {
Date date = (Date) object;
return new StringBuilder().append("\"").append(String.valueOf(date.getTime())).append("\"");
}
if (!TRACER_SERIALISE_INTERNALS) {
String className = object.getClass().getName();
List<String> ignoredPackages;
try {
ignoredPackages = Files.readAllLines(Paths.get(TRACER_IGNORED_PACKAGES));
} catch (IOException ex) {
ignoredPackages = new ArrayList<>();
}
String[] internals = {"java.", "org.ietf.", "org.omg.", "org.w3c.", "org.xml."};
ignoredPackages.addAll(Arrays.asList(internals));
if (ignoredPackages.stream().anyMatch(ignoredPackage -> className.startsWith(ignoredPackage))) {
return getReference(object);
}
try {
List<String> whiteList = Files.readAllLines(Paths.get(TRACER_WHITELIST));
if (whiteList.stream().noneMatch(ignoredPackage -> className.startsWith(ignoredPackage))) {
log(System.currentTimeMillis() + " [JSONSerialiser] proceeding " + className);
}
} catch (IOException ex) {
}
}
if (this.visited.stream().parallel().anyMatch(visited -> visited == object)) {
return getReference(object);
}
int index = this.visited.size() + 1;
this.visited.add(object);
StringBuilder serialiseBean = serialiseBean(object);
while (index < this.visited.size()) {
this.visited.remove(index);
}
return serialiseBean;
} catch (ConcurrentModificationException exception) {
System.err.println("[JSONSerialiser] ConcurrentModificationException");
return new StringBuilder().append("\"JSON_CONCURRENT_MODIFICATION\"");
} catch (Exception exception) {
System.err.println("[JSONSerialiser] wrap exception: " + exception);
exception.printStackTrace();
return new StringBuilder().append("\"JSON_SERIALISE_EXCEPTION\"");
} catch (StackOverflowError exception) {
System.err.println("[JSONSerialiser] stack overflow");
return new StringBuilder().append("\"JSON_STACK_OVERFLOW\"");
}
}
private StringBuilder serialiseBean(Object bean) {
StringBuilder stringBuilder = new StringBuilder();
if (bean == null) {
stringBuilder.append("null");
return stringBuilder;
}
Class<?> klass = bean.getClass();
List<Field> fields = new ArrayList<>();
fields = Utils.getAllFields(fields, klass);
stringBuilder.append("{");
for (Iterator<Field> it = fields.iterator(); it.hasNext();) {
Field field = it.next();
try {
field.setAccessible(true);
Object result = field.get(bean);
if (result == null) {
continue;
}
stringBuilder.append("\"").append(field.getName()).append("\"");
stringBuilder.append(":");
StringBuilder wrap = wrap(result);
stringBuilder.append(wrap);
} catch (Exception ignore) {
System.err.println("[JSONSerialiser] field exception: " + ignore);
stringBuilder.append("\"").append("JSON-FIELD-EXCEPTION").append("\"");
}
if (it.hasNext()) {
stringBuilder.append(",");
}
}
stringBuilder.append("}");
return stringBuilder;
}
private StringBuilder serialiseMap(Map map) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("{");
try {
for (Iterator<Map.Entry<?, ?>> iterator = map.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<?, ?> entry = iterator.next();
Object key = entry.getKey();
Object value = entry.getValue();
if (key == null || value == null) {
continue;
}
StringBuilder serialisedKey;
if (key instanceof String) {
serialisedKey = serialiseString((String) key);
} else if (this.visited.stream().parallel().anyMatch(visited -> visited == key)) {
serialisedKey = serialiseString(getReference(key));
} else {
serialisedKey = serialiseString(JSONSerialiser.serialise(key));
}
StringBuilder wrappedValue = wrap(value);
stringBuilder.append(serialisedKey).append(":").append(wrappedValue);
if (iterator.hasNext()) {
stringBuilder.append(",");
}
}
} catch (Exception ex) {
System.err.println("[JSONSerialiser] map serialise exception: " + ex);
return new StringBuilder().append("{\"e\":\"JSON_MAP_EXCEPTION\"}");
}
return stringBuilder.append("}");
}
private StringBuilder serialiseHashMap(HashMap map) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("{");
try {
TreeMap<String, StringBuilder> serialisedMap = new TreeMap<>();
for (Iterator<Map.Entry<?, ?>> iterator = map.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<?, ?> entry = iterator.next();
Object key = entry.getKey();
Object value = entry.getValue();
if (key == null || value == null) {
continue;
}
String serialisedKey;
if (key instanceof String) {
serialisedKey = serialiseString((String) key).toString();
} else if (this.visited.stream().parallel().anyMatch(visited -> visited == key)) {
serialisedKey = serialiseString(getReference(key)).toString();
} else {
serialisedKey = serialiseString(JSONSerialiser.serialise(key)).toString();
}
StringBuilder wrappedValue = wrap(value);
serialisedMap.put(serialisedKey, wrappedValue);
}
for (Iterator<Map.Entry<String, StringBuilder>> iterator = serialisedMap.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<String, StringBuilder> entry = iterator.next();
String key = entry.getKey();
StringBuilder value = entry.getValue();
stringBuilder.append(key).append(":").append(value);
if (iterator.hasNext()) {
stringBuilder.append(",");
}
}
} catch (Exception ex) {
System.err.println("[JSONSerialiser] hashmap serialise exception: " + ex);
return new StringBuilder().append("{\"e\":\"JSON_HASHMAP_EXCEPTION\"}");
}
return stringBuilder.append("}");
}
private StringBuilder serialiseArray(Object array) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
try {
int length = Array.getLength(array);
for (int i = 0; i < length;) {
Object object = Array.get(array, i);
StringBuilder wrap = wrap(object);
stringBuilder.append(wrap);
if (++i < length) {
stringBuilder.append(",");
}
}
} catch (Exception ex) {
System.err.println("[JSONSerialiser] array serialise exception: " + ex);
return new StringBuilder().append("[\"JSON_ARRAY_EXCEPTION\"]");
}
return stringBuilder.append("]");
}
private StringBuilder serialiseCollection(Collection collection) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
try {
for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
Object object = iterator.next();
StringBuilder wrap = wrap(object);
stringBuilder.append(wrap);
if (iterator.hasNext()) {
stringBuilder.append(",");
}
}
} catch (Exception ex) {
String toString = ex.toString();
if (!toString.startsWith("org.hibernate")) {
System.err.println("[JSONSerialiser] collection serialise exception: " + toString);
return new StringBuilder().append("[\"JSON_COLLECTION_HIBERNATE_EXCEPTION\"]");
}
return new StringBuilder().append("[\"JSON_COLLECTION_EXCEPTION\"]");
}
return stringBuilder.append("]");
}
private StringBuilder serialiseHashSet(HashSet hashSet) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
try {
List<String> serialisedCollection = new ArrayList<>();
for (Iterator iterator = hashSet.iterator(); iterator.hasNext();) {
Object object = iterator.next();
StringBuilder wrap = wrap(object);
serialisedCollection.add(wrap.toString());
}
Collections.sort(serialisedCollection);
for (Iterator<String> iterator = serialisedCollection.iterator(); iterator.hasNext();) {
String next = iterator.next();
stringBuilder.append(next);
if (iterator.hasNext()) {
stringBuilder.append(",");
}
}
} catch (Exception ex) {
String toString = ex.toString();
if (!toString.startsWith("org.hibernate")) {
System.err.println("[JSONSerialiser] hashset serialise exception: " + toString);
return new StringBuilder().append("[\"JSON_HASHSET_HIBERNATE_EXCEPTION\"]");
}
return new StringBuilder().append("[\"JSON_HASHSET_EXCEPTION\"]");
}
return stringBuilder.append("]");
}
private StringBuilder serialiseString(CharSequence string) {
StringBuilder stringBuilder = new StringBuilder();
if (string == null || string.length() == 0) {
return stringBuilder.append("\"\"");
}
stringBuilder.append('"');
try {
char b;
char c = 0;
String hexadecimal;
int i;
int len = string.length();
for (i = 0; i < len; i++) {
b = c;
c = string.charAt(i);
switch (c) {
case '\\':
case '"':
stringBuilder.append('\\');
stringBuilder.append(c);
break;
case '/':
if (b == '<') {
stringBuilder.append('\\');
}
stringBuilder.append(c);
break;
case '\b':
stringBuilder.append("\\b");
break;
case '\t':
stringBuilder.append("\\t");
break;
case '\n':
stringBuilder.append("\\n");
break;
case '\f':
stringBuilder.append("\\f");
break;
case '\r':
stringBuilder.append("\\r");
break;
default:
if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
|| (c >= '\u2000' && c < '\u2100')) {
stringBuilder.append("\\u");
hexadecimal = Integer.toHexString(c);
stringBuilder.append("0000", 0, 4 - hexadecimal.length());
stringBuilder.append(hexadecimal);
} else {
stringBuilder.append(c);
}
}
}
} catch (Exception ex) {
System.err.println("[JSONSerialiser] string serialise exception: " + ex);
return new StringBuilder().append("\"JSON_STRING_EXCEPTION\"");
}
return stringBuilder.append('"');
}
private void log(String message) {
if (TRACER_LOG == null) {
return;
}
try (FileWriter fileWriter = new FileWriter(TRACER_LOG, true)) {
fileWriter.write(message + "\n");
} catch (IOException ex) {
}
}
private static StringBuilder getReference(Object object) {
return new StringBuilder().append("{\"r\":\"@")
.append(object.getClass().getName())
.append("\"}");
}
}