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.IOException;
import java.io.Writer;
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.ConcurrentModificationException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
*
* @author romulo
*/
public class JSONSerialiser {
private final Collection<Object> visited;
private final Writer writer;
private JSONSerialiser(Writer writer) {
this.visited = new ArrayList<>();
this.writer = writer;
}
public static void serialise(Object bean, Writer writer, boolean cyclicFields) throws IOException {
JSONSerialiser jsonSerialiser = new JSONSerialiser(writer);
jsonSerialiser.serialiseBean(bean, cyclicFields);
}
public static void serialise(Object bean, Writer writer) throws IOException {
serialise(bean, writer, true);
}
public static String serialise(Object bean, boolean cyclicFields) {
StringBuilder stringBuilder = new StringBuilder();
try {
serialise(bean, new StringBuilderWriter(stringBuilder), cyclicFields);
} catch (IOException ex) {
}
return stringBuilder.toString();
}
public static String serialise(Object bean) {
return serialise(bean, true);
}
public static String serialiseCyclicObject(Object bean) {
return serialise(bean);
}
public static String serialiseAcyclicObject(Object bean) {
return serialise(bean, false);
}
public static void serialiseAcyclicObject(Object bean, Writer writer) throws IOException {
serialise(bean, writer, false);
}
private void resetCycleBreaker() {
this.visited.clear();
}
private void serialiseBean(Object bean, boolean cyclicFields) throws IOException {
if (bean == null) {
this.writer.append("null");
return;
}
Class<?> klass = bean.getClass();
List<Field> fields = new ArrayList<>();
fields = getAllFields(fields, klass);
this.writer.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;
}
if (!cyclicFields) {
resetCycleBreaker();
}
this.writer.append("\"").append(field.getName()).append("\"");
this.writer.append(":");
wrap(result);
} catch (Exception ignore) {
System.err.println("[JSONSerialiser] field exception: " + ignore);
this.writer.append("\"").append("JSON-FIELD-EXCEPTION").append("\"");
}
if (it.hasNext()) {
this.writer.append(",");
}
}
this.writer.append("}");
}
private void serialiseBean(Object bean) throws IOException {
serialiseBean(bean, true);
}
private 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 void wrap(Object object) throws IOException {
try {
if (object == null) {
this.writer.append("null");
return;
}
if (object instanceof Boolean) {
this.writer.append("\"").append(String.valueOf(object)).append("\"");
return;
}
if (object instanceof Character) {
serialiseString(String.valueOf(object));
return;
}
if (object instanceof String) {
String string = (String) object;
serialiseString(string);
return;
}
if (object instanceof Number) {
this.writer.append("\"").append(String.valueOf(object)).append("\"");
return;
}
if (object instanceof Enum) {
serialiseString(((Enum<?>) object).name());
return;
}
if (object instanceof Collection) {
Collection<?> collection = (Collection<?>) object;
serialiseCollection(collection);
return;
}
if (object.getClass().isArray()) {
serialiseArray(object);
return;
}
if (object instanceof Map) {
Map<?, ?> map = (Map<?, ?>) object;
serialiseMap(map);
return;
}
if (object instanceof Date) {
Date date = (Date) object;
this.writer.append("\"").append(String.valueOf(date.getTime())).append("\"");
return;
}
String serialiseInternals = System.getenv("TRACER_SERIALISE_INTERNALS");
if (serialiseInternals == null) {
serialiseInternals = "true";
}
if (serialiseInternals.equals("false")) {
Package objectPackage = object.getClass().getPackage();
String objectPackageName = objectPackage != null ? objectPackage.getName() : "";
String ignoredPackagesPath = System.getenv("TRACER_IGNORED_PACKAGES");
if (ignoredPackagesPath == null) {
ignoredPackagesPath = "./ignored";
}
List<String> ignoredPackages;
try {
ignoredPackages = Files.readAllLines(Paths.get(ignoredPackagesPath));
} catch (IOException ex) {
ignoredPackages = new ArrayList<>();
}
String[] internals = {"java.", "org.ietf.", "org.omg.", "org.w3c.", "org.xml."};
ignoredPackages.addAll(Arrays.asList(internals));
for (String ignoredPackage : ignoredPackages) {
if (objectPackageName.startsWith(ignoredPackage)) {
this.writer.append("\"").append(String.valueOf(object)).append("\"");
return;
}
}
}
if (visited.stream().parallel().anyMatch((visited) -> (visited == object))) {
try {
this.writer.append("{\"r\":\"").append(object.getClass().getName() + "@" + object.hashCode()).append("\"}");
} catch (Exception e) {
this.writer.append("{\"r\":\"").append(object.getClass().getName() + "@" + object.getClass().hashCode()).append("\"}");
} finally {
return;
}
}
visited.add(object);
serialiseBean(object);
} catch (ConcurrentModificationException exception) {
System.err.println("[JSONSerialiser] ConcurrentModificationException");
this.writer.append("{\"e\":\"JSON_CONCURRENT_MODIFICATION\"}");
} catch (Exception exception) {
System.err.println("[JSONSerialiser] wrap exception: " + exception);
exception.printStackTrace();
this.writer.append("{\"e\":\"JSON_SERIALISE_EXCEPTION\"}");
}
}
private void serialiseMap(Map map) throws IOException {
try {
this.writer.append("{");
for (Iterator<?> it = map.keySet().iterator(); it.hasNext();) {
Object key = it.next();
if (key == null || map.get(key) == null) {
continue;
}
if (!(key instanceof String)) {
this.writer.append("\"");
wrap(key);
this.writer.append("\"");
} else {
wrap(key);
}
this.writer.append(":");
wrap(map.get(key));
if (it.hasNext()) {
this.writer.append(",");
}
}
} catch (Exception ex) {
System.err.println("[JSONSerialiser] map serialise exception: " + ex);
} finally {
this.writer.append("}");
}
}
private void serialiseArray(Object array) throws IOException {
this.writer.append("[");
try {
int length = Array.getLength(array);
for (int i = 0; i < length;) {
Object object = Array.get(array, i);
wrap(object);
if (++i < length) {
this.writer.append(",");
}
}
} catch (Exception ex) {
System.err.println("[JSONSerialiser] array serialise exception: " + ex);
} finally {
this.writer.append("]");
}
}
private void serialiseCollection(Collection collection) throws IOException {
try {
this.writer.append("[");
Iterator it = collection.iterator();
while (it.hasNext()) {
Object object = it.next();
wrap(object);
if (it.hasNext()) {
this.writer.append(",");
}
}
} catch (Exception ex) {
System.err.println("[JSONSerialiser] collection serialise exception: " + ex);
} finally {
this.writer.append("]");
}
}
private void serialiseString(String string) throws IOException {
if (string == null || string.isEmpty()) {
this.writer.append("\"\"");
return;
}
try {
this.writer.append('"');
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 '"':
this.writer.append('\\');
this.writer.append(c);
break;
case '/':
if (b == '<') {
this.writer.append('\\');
}
this.writer.append(c);
break;
case '\b':
this.writer.append("\\b");
break;
case '\t':
this.writer.append("\\t");
break;
case '\n':
this.writer.append("\\n");
break;
case '\f':
this.writer.append("\\f");
break;
case '\r':
this.writer.append("\\r");
break;
default:
if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
|| (c >= '\u2000' && c < '\u2100')) {
this.writer.append("\\u");
hexadecimal = Integer.toHexString(c);
this.writer.append("0000", 0, 4 - hexadecimal.length());
this.writer.append(hexadecimal);
} else {
this.writer.append(c);
}
}
}
} catch (Exception ex) {
System.err.println("[JSONSerialiser] string serialise exception: " + ex);
} finally {
this.writer.append('"');
}
}
}