mirror of
https://github.com/plexusorg/toml4j.git
synced 2025-02-11 11:40:27 +00:00
Implement serialization.
This provides functionality to convert populated Toml instances and arbitrary objects into TOML.
This commit is contained in:
parent
5402ce22f3
commit
e7d7de7ae5
15 changed files with 863 additions and 16 deletions
28
README.md
28
README.md
|
@ -204,6 +204,34 @@ Long tableD = toml.getLong("table.d"); // returns null, not 5, because of shallo
|
||||||
Long arrayD = toml.getLong("array[0].d"); // returns 3
|
Long arrayD = toml.getLong("array[0].d"); // returns 3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Serialization
|
||||||
|
|
||||||
|
You can serialize a `Toml` or any arbitrary object to a TOML string.
|
||||||
|
|
||||||
|
Once you have populated a `Toml` via `Toml.parse()`, you can serialize it back to TOML.
|
||||||
|
|
||||||
|
```java
|
||||||
|
Toml toml = new Toml().parse("a=1");
|
||||||
|
String tomlString = toml.serialize();
|
||||||
|
```
|
||||||
|
|
||||||
|
Or you can serialize any object.
|
||||||
|
|
||||||
|
```java
|
||||||
|
class AClass {
|
||||||
|
int anInt = 1;
|
||||||
|
int[] anArray = { 2, 3 };
|
||||||
|
}
|
||||||
|
|
||||||
|
String tomlString = Toml.serializeFrom(new AClass());
|
||||||
|
|
||||||
|
/*
|
||||||
|
yields:
|
||||||
|
anInt = 1
|
||||||
|
anArray = [ 2, 3 ]
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
|
|
||||||
Date precision is limited to milliseconds.
|
Date precision is limited to milliseconds.
|
||||||
|
|
69
src/main/java/com/moandjiezana/toml/ArraySerializer.java
Normal file
69
src/main/java/com/moandjiezana/toml/ArraySerializer.java
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
abstract class ArraySerializer implements Serializer {
|
||||||
|
static protected boolean isArrayish(Object value) {
|
||||||
|
return value instanceof Collection || value.getClass().isArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPrimitiveType() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isArrayOfPrimitive(Object array) {
|
||||||
|
Object first = peek(array);
|
||||||
|
if (first != null) {
|
||||||
|
Serializer serializer = Serializers.findSerializerFor(first);
|
||||||
|
return serializer.isPrimitiveType() || isArrayish(first);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected Collection normalize(Object value) {
|
||||||
|
Collection collection;
|
||||||
|
|
||||||
|
if (value.getClass().isArray()) {
|
||||||
|
// Arrays.asList() interprets an array as a single element,
|
||||||
|
// so convert it to a list by hand
|
||||||
|
collection = new ArrayList<Object>(Array.getLength(value));
|
||||||
|
for (int i = 0; i < Array.getLength(value); i++) {
|
||||||
|
Object elem = Array.get(value, i);
|
||||||
|
collection.add(elem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collection = (Collection) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Object peek(Object value) {
|
||||||
|
if (value.getClass().isArray()) {
|
||||||
|
if (Array.getLength(value) > 0) {
|
||||||
|
return Array.get(value, 0);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Collection collection = (Collection) value;
|
||||||
|
if (collection.size() > 0) {
|
||||||
|
return collection.iterator().next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
25
src/main/java/com/moandjiezana/toml/BooleanSerializer.java
Normal file
25
src/main/java/com/moandjiezana/toml/BooleanSerializer.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
class BooleanSerializer implements Serializer {
|
||||||
|
static final Serializer BOOLEAN_SERIALIZER = new BooleanSerializer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSerialize(Object value) {
|
||||||
|
return Boolean.class.isInstance(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Object value, SerializerContext context) {
|
||||||
|
context.serialized.append(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPrimitiveType() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
29
src/main/java/com/moandjiezana/toml/DateSerializer.java
Normal file
29
src/main/java/com/moandjiezana/toml/DateSerializer.java
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
class DateSerializer implements Serializer {
|
||||||
|
static final Serializer DATE_SERIALIZER = new DateSerializer();
|
||||||
|
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:m:ssXXX");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSerialize(Object value) {
|
||||||
|
return value instanceof Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Object value, SerializerContext context) {
|
||||||
|
context.serialized.append(dateFormat.format(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPrimitiveType() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
98
src/main/java/com/moandjiezana/toml/MapSerializer.java
Normal file
98
src/main/java/com/moandjiezana/toml/MapSerializer.java
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static com.moandjiezana.toml.PrimitiveArraySerializer.PRIMITIVE_ARRAY_SERIALIZER;
|
||||||
|
import static com.moandjiezana.toml.TableArraySerializer.TABLE_ARRAY_SERIALIZER;
|
||||||
|
|
||||||
|
class MapSerializer implements Serializer {
|
||||||
|
static final Serializer MAP_SERIALIZER = new MapSerializer();
|
||||||
|
|
||||||
|
private static final Pattern requiredQuotingPattern = Pattern.compile("^.*[^A-Za-z\\d_-].*$");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSerialize(Object value) {
|
||||||
|
return value instanceof Map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Object value, SerializerContext context) {
|
||||||
|
Map from = (Map) value;
|
||||||
|
|
||||||
|
if (hasPrimitiveValues(from)) {
|
||||||
|
context.serializeKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render primitive types and arrays of primitive first so they are
|
||||||
|
// grouped under the same table (if there is one)
|
||||||
|
for (Object key : from.keySet()) {
|
||||||
|
Object fromValue = from.get(key);
|
||||||
|
if (fromValue == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serializer serializer = Serializers.findSerializerFor(fromValue);
|
||||||
|
if (serializer.isPrimitiveType()) {
|
||||||
|
context.indent();
|
||||||
|
context.serialized.append(quoteKey(key)).append(" = ");
|
||||||
|
serializer.serialize(fromValue, context);
|
||||||
|
context.serialized.append('\n');
|
||||||
|
} else if (serializer == PRIMITIVE_ARRAY_SERIALIZER) {
|
||||||
|
context.serialized.append(quoteKey(key)).append(" = ");
|
||||||
|
serializer.serialize(fromValue, context);
|
||||||
|
context.serialized.append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now render (sub)tables and arrays of tables
|
||||||
|
for (Object key : from.keySet()) {
|
||||||
|
Object fromValue = from.get(key);
|
||||||
|
if (fromValue == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serializer serializer = Serializers.findSerializerFor(fromValue);
|
||||||
|
if (serializer.isTable() || serializer == TABLE_ARRAY_SERIALIZER) {
|
||||||
|
serializer.serialize(fromValue, context.extend(quoteKey(key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPrimitiveType() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String quoteKey(Object key) {
|
||||||
|
String stringKey = key.toString();
|
||||||
|
Matcher matcher = requiredQuotingPattern.matcher(stringKey);
|
||||||
|
if (matcher.matches()) {
|
||||||
|
stringKey = "\"" + stringKey + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasPrimitiveValues(Map values) {
|
||||||
|
for (Object key : values.keySet()) {
|
||||||
|
Object fromValue = values.get(key);
|
||||||
|
if (fromValue == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serializer serializer = Serializers.findSerializerFor(fromValue);
|
||||||
|
if (serializer.isPrimitiveType() || serializer == PRIMITIVE_ARRAY_SERIALIZER) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
25
src/main/java/com/moandjiezana/toml/NumberSerializer.java
Normal file
25
src/main/java/com/moandjiezana/toml/NumberSerializer.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
class NumberSerializer implements Serializer {
|
||||||
|
static final Serializer NUMBER_SERIALIZER = new NumberSerializer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSerialize(Object value) {
|
||||||
|
return Number.class.isInstance(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Object value, SerializerContext context) {
|
||||||
|
context.serialized.append(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPrimitiveType() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
76
src/main/java/com/moandjiezana/toml/ObjectSerializer.java
Normal file
76
src/main/java/com/moandjiezana/toml/ObjectSerializer.java
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static com.moandjiezana.toml.MapSerializer.MAP_SERIALIZER;
|
||||||
|
|
||||||
|
class ObjectSerializer implements Serializer {
|
||||||
|
static final Serializer OBJECT_SERIALIZER = new ObjectSerializer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSerialize(Object value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Object value, SerializerContext context) {
|
||||||
|
Map<String, Object> to = new LinkedHashMap<String, Object>();
|
||||||
|
Set<Field> fields = getFieldsForClass(value.getClass());
|
||||||
|
for (Field field : fields) {
|
||||||
|
to.put(field.getName(), getFieldValue(field, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
MAP_SERIALIZER.serialize(to, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPrimitiveType() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static private Set<Field> getFieldsForClass(Class cls) {
|
||||||
|
Set<Field> fields = new LinkedHashSet<Field>(Arrays.asList(cls.getDeclaredFields()));
|
||||||
|
|
||||||
|
getSuperClassFields(cls.getSuperclass(), fields);
|
||||||
|
|
||||||
|
// Skip final fields
|
||||||
|
fields.removeIf(new Predicate<Field>() {
|
||||||
|
@Override
|
||||||
|
public boolean test(Field field) {
|
||||||
|
return Modifier.isFinal(field.getModifiers());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
static private void getSuperClassFields(Class cls, Set<Field> fields) {
|
||||||
|
if (cls == Object.class) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.addAll(Arrays.asList(cls.getDeclaredFields()));
|
||||||
|
getSuperClassFields(cls.getSuperclass(), fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
static private Object getFieldValue(Field field, Object o) {
|
||||||
|
boolean isAccessible = field.isAccessible();
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object value = null;
|
||||||
|
try {
|
||||||
|
value = field.get(o);
|
||||||
|
} catch (IllegalAccessException ignored) {
|
||||||
|
}
|
||||||
|
field.setAccessible(isAccessible);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
class PrimitiveArraySerializer extends ArraySerializer {
|
||||||
|
static final Serializer PRIMITIVE_ARRAY_SERIALIZER = new PrimitiveArraySerializer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSerialize(Object value) {
|
||||||
|
return isArrayish(value) && isArrayOfPrimitive(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Object value, SerializerContext context) {
|
||||||
|
Collection values = normalize(value);
|
||||||
|
|
||||||
|
context.serialized.append("[ ");
|
||||||
|
boolean first = true;
|
||||||
|
for (Object elem : values) {
|
||||||
|
if (!first) {
|
||||||
|
context.serialized.append(", ");
|
||||||
|
}
|
||||||
|
Serializers.serialize(elem, context);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
context.serialized.append(" ]");
|
||||||
|
}
|
||||||
|
}
|
11
src/main/java/com/moandjiezana/toml/Serializer.java
Normal file
11
src/main/java/com/moandjiezana/toml/Serializer.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
interface Serializer {
|
||||||
|
boolean canSerialize(Object value);
|
||||||
|
|
||||||
|
void serialize(Object value, SerializerContext context);
|
||||||
|
|
||||||
|
boolean isPrimitiveType();
|
||||||
|
|
||||||
|
boolean isTable();
|
||||||
|
}
|
50
src/main/java/com/moandjiezana/toml/SerializerContext.java
Normal file
50
src/main/java/com/moandjiezana/toml/SerializerContext.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
class SerializerContext {
|
||||||
|
private String key = "";
|
||||||
|
private boolean isArrayOfTable = false;
|
||||||
|
StringBuilder serialized = new StringBuilder();
|
||||||
|
|
||||||
|
SerializerContext(String key, StringBuilder serialized) {
|
||||||
|
this.key = key;
|
||||||
|
this.serialized = serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializerContext() {
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializerContext extend(String newKey) {
|
||||||
|
String fullKey = key + (key.isEmpty() ? newKey : "." + newKey);
|
||||||
|
|
||||||
|
return new SerializerContext(fullKey, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializerContext extend() {
|
||||||
|
return new SerializerContext(key, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
void serializeKey() {
|
||||||
|
if (key.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialized.length() > 0) {
|
||||||
|
serialized.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isArrayOfTable) {
|
||||||
|
serialized.append("[[").append(key).append("]]\n");
|
||||||
|
} else {
|
||||||
|
serialized.append('[').append(key).append("]\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void indent() {
|
||||||
|
serialized.append(key.isEmpty() ? "" : " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializerContext setIsArrayOfTable(boolean isArrayOfTable) {
|
||||||
|
this.isArrayOfTable = isArrayOfTable;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
38
src/main/java/com/moandjiezana/toml/Serializers.java
Normal file
38
src/main/java/com/moandjiezana/toml/Serializers.java
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
import static com.moandjiezana.toml.BooleanSerializer.BOOLEAN_SERIALIZER;
|
||||||
|
import static com.moandjiezana.toml.DateSerializer.DATE_SERIALIZER;
|
||||||
|
import static com.moandjiezana.toml.MapSerializer.MAP_SERIALIZER;
|
||||||
|
import static com.moandjiezana.toml.NumberSerializer.NUMBER_SERIALIZER;
|
||||||
|
import static com.moandjiezana.toml.ObjectSerializer.OBJECT_SERIALIZER;
|
||||||
|
import static com.moandjiezana.toml.PrimitiveArraySerializer.PRIMITIVE_ARRAY_SERIALIZER;
|
||||||
|
import static com.moandjiezana.toml.StringSerializer.STRING_SERIALIZER;
|
||||||
|
import static com.moandjiezana.toml.TableArraySerializer.TABLE_ARRAY_SERIALIZER;
|
||||||
|
|
||||||
|
abstract class Serializers {
|
||||||
|
private static final Serializer[] SERIALIZERS = {
|
||||||
|
STRING_SERIALIZER, NUMBER_SERIALIZER, BOOLEAN_SERIALIZER, DATE_SERIALIZER,
|
||||||
|
MAP_SERIALIZER, PRIMITIVE_ARRAY_SERIALIZER, TABLE_ARRAY_SERIALIZER
|
||||||
|
};
|
||||||
|
|
||||||
|
static Serializer findSerializerFor(Object value) {
|
||||||
|
for (Serializer serializer : SERIALIZERS) {
|
||||||
|
if (serializer.canSerialize(value)) {
|
||||||
|
return serializer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OBJECT_SERIALIZER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String serialize(Object value) {
|
||||||
|
SerializerContext context = new SerializerContext();
|
||||||
|
serialize(value, context);
|
||||||
|
|
||||||
|
return context.serialized.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serialize(Object value, SerializerContext context) {
|
||||||
|
findSerializerFor(value).serialize(value, context);
|
||||||
|
}
|
||||||
|
}
|
56
src/main/java/com/moandjiezana/toml/StringSerializer.java
Normal file
56
src/main/java/com/moandjiezana/toml/StringSerializer.java
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
class StringSerializer implements Serializer {
|
||||||
|
static final Serializer STRING_SERIALIZER = new StringSerializer();
|
||||||
|
|
||||||
|
static private final String[] specialCharacterEscapes = new String[93];
|
||||||
|
|
||||||
|
static {
|
||||||
|
specialCharacterEscapes[0x08] = "\\b";
|
||||||
|
specialCharacterEscapes[0x09] = "\\t";
|
||||||
|
specialCharacterEscapes[0x0A] = "\\n";
|
||||||
|
specialCharacterEscapes[0x0C] = "\\f";
|
||||||
|
specialCharacterEscapes[0x0D] = "\\r";
|
||||||
|
specialCharacterEscapes[0x22] = "\\\"";
|
||||||
|
specialCharacterEscapes[0x5C] = "\\";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSerialize(Object value) {
|
||||||
|
return value.getClass().isAssignableFrom(String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Object value, SerializerContext context) {
|
||||||
|
context.serialized.append('"');
|
||||||
|
escapeUnicode(value.toString(), context.serialized);
|
||||||
|
context.serialized.append('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPrimitiveType() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void escapeUnicode(String in, StringBuilder out) {
|
||||||
|
for (int i = 0; i < in.length(); i++) {
|
||||||
|
int codePoint = in.codePointAt(i);
|
||||||
|
if (codePoint < specialCharacterEscapes.length && specialCharacterEscapes[codePoint] != null) {
|
||||||
|
out.append(specialCharacterEscapes[codePoint]);
|
||||||
|
} else if (codePoint > 0x1f && codePoint < 0x7f) {
|
||||||
|
out.append(Character.toChars(codePoint));
|
||||||
|
} else if (codePoint <= 0xFFFF) {
|
||||||
|
out.append(String.format("\\u%04X", codePoint));
|
||||||
|
} else {
|
||||||
|
out.append(String.format("\\U%08X", codePoint));
|
||||||
|
// Skip the low surrogate, which will be the next in the code point sequence.
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
class TableArraySerializer extends ArraySerializer {
|
||||||
|
static final Serializer TABLE_ARRAY_SERIALIZER = new TableArraySerializer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canSerialize(Object value) {
|
||||||
|
return isArrayish(value) && !isArrayOfPrimitive(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Object value, SerializerContext context) {
|
||||||
|
Collection values = normalize(value);
|
||||||
|
|
||||||
|
SerializerContext subContext = context.extend().setIsArrayOfTable(true);
|
||||||
|
|
||||||
|
for (Object elem : values) {
|
||||||
|
Serializers.serialize(elem, subContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +1,11 @@
|
||||||
package com.moandjiezana.toml;
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Provides access to the keys and tables in a TOML data source.</p>
|
* <p>Provides access to the keys and tables in a TOML data source.</p>
|
||||||
*
|
*
|
||||||
|
@ -282,6 +269,29 @@ public class Toml {
|
||||||
return DEFAULT_GSON.fromJson(json, targetClass);
|
return DEFAULT_GSON.fromJson(json, targetClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the values of this Toml instance into TOML.
|
||||||
|
*
|
||||||
|
* @return a string containing the TOML representation of this Toml instance.
|
||||||
|
*/
|
||||||
|
public String serialize() {
|
||||||
|
return Serializers.serialize(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes an Object into TOML.
|
||||||
|
*
|
||||||
|
* The input can comprise arbitrarily nested combinations of Java primitive types,
|
||||||
|
* other {@link Object}s, {@link Map}s, {@link List}s, and Arrays. {@link Object}s and {@link Map}s
|
||||||
|
* are serialized to TOML tables, and {@link List}s and Array to TOML arrays.
|
||||||
|
*
|
||||||
|
* @param from the object to be serialized
|
||||||
|
* @return a string containing the TOML representation of the given Object
|
||||||
|
*/
|
||||||
|
public static String serializeFrom(Object from) {
|
||||||
|
return Serializers.serialize(from);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Object get(String key) {
|
private Object get(String key) {
|
||||||
if (values.containsKey(key)) {
|
if (values.containsKey(key)) {
|
||||||
|
|
281
src/test/java/com/moandjiezana/toml/SerializerTest.java
Normal file
281
src/test/java/com/moandjiezana/toml/SerializerTest.java
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
package com.moandjiezana.toml;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class SerializerTest {
|
||||||
|
@Test
|
||||||
|
public void serializesPrimitiveTypes() {
|
||||||
|
class TestClass {
|
||||||
|
public String aString;
|
||||||
|
int anInt;
|
||||||
|
protected float aFloat;
|
||||||
|
private double aDouble;
|
||||||
|
boolean aBoolean;
|
||||||
|
final int aFinalInt = 1; // Should be skipped
|
||||||
|
Date aDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestClass o = new TestClass();
|
||||||
|
o.aString = "hello";
|
||||||
|
o.anInt = 4;
|
||||||
|
o.aFloat = 1.23f;
|
||||||
|
o.aDouble = -5.43;
|
||||||
|
o.aBoolean = false;
|
||||||
|
|
||||||
|
String theDate = "2015-05-31T08:44:03-07:00";
|
||||||
|
Toml dateToml = new Toml().parse("a_date = " + theDate);
|
||||||
|
o.aDate = dateToml.getDate("a_date");
|
||||||
|
|
||||||
|
String serialized = Toml.serializeFrom(o);
|
||||||
|
String expected = "aString = \"hello\"\n" +
|
||||||
|
"anInt = 4\n" +
|
||||||
|
"aFloat = 1.23\n" +
|
||||||
|
"aDouble = -5.43\n" +
|
||||||
|
"aBoolean = false\n" +
|
||||||
|
"aDate = " + theDate + "\n";
|
||||||
|
|
||||||
|
assertEquals(expected, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializesNestedMap() {
|
||||||
|
class SubChild {
|
||||||
|
int anInt;
|
||||||
|
}
|
||||||
|
class Child {
|
||||||
|
SubChild subChild;
|
||||||
|
int anInt;
|
||||||
|
}
|
||||||
|
class Parent {
|
||||||
|
Map<String, Object> aMap;
|
||||||
|
Child child;
|
||||||
|
boolean aBoolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent parent = new Parent();
|
||||||
|
parent.aMap = new LinkedHashMap<String, Object>();
|
||||||
|
parent.aMap.put("foo", 1);
|
||||||
|
parent.aMap.put("bar", "value1");
|
||||||
|
parent.aMap.put("baz.x", true);
|
||||||
|
parent.child = new Child();
|
||||||
|
parent.child.anInt = 2;
|
||||||
|
parent.child.subChild = new SubChild();
|
||||||
|
parent.child.subChild.anInt = 4;
|
||||||
|
parent.aBoolean = true;
|
||||||
|
|
||||||
|
String serialized = Toml.serializeFrom(parent);
|
||||||
|
String expected = "aBoolean = true\n\n" +
|
||||||
|
"[aMap]\n" +
|
||||||
|
" foo = 1\n" +
|
||||||
|
" bar = \"value1\"\n" +
|
||||||
|
" \"baz.x\" = true\n\n" +
|
||||||
|
"[child]\n" +
|
||||||
|
" anInt = 2\n\n" +
|
||||||
|
"[child.subChild]\n" +
|
||||||
|
" anInt = 4\n";
|
||||||
|
assertEquals(expected, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializesArrayOfPrimitive() {
|
||||||
|
class ArrayTest {
|
||||||
|
int[] array = {1, 2, 3};
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayTest arrayTest = new ArrayTest();
|
||||||
|
String serialized = Toml.serializeFrom(arrayTest);
|
||||||
|
String expected = "array = [ 1, 2, 3 ]\n";
|
||||||
|
assertEquals(expected, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializesArrayOfTables() {
|
||||||
|
class Table {
|
||||||
|
int anInt;
|
||||||
|
|
||||||
|
Table(int anInt) {
|
||||||
|
this.anInt = anInt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Config {
|
||||||
|
Table[] table;
|
||||||
|
}
|
||||||
|
Config config = new Config();
|
||||||
|
config.table = new Table[]{new Table(1), new Table(2)};
|
||||||
|
|
||||||
|
String serialized = Toml.serializeFrom(config);
|
||||||
|
String expected = "[[table]]\n" +
|
||||||
|
" anInt = 1\n\n" +
|
||||||
|
"[[table]]\n" +
|
||||||
|
" anInt = 2\n";
|
||||||
|
assertEquals(expected, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializesArrayOfArray() {
|
||||||
|
class ArrayTest {
|
||||||
|
int[][] array = {{1, 2, 3}, {4, 5, 6}};
|
||||||
|
}
|
||||||
|
ArrayTest arrayTest = new ArrayTest();
|
||||||
|
|
||||||
|
String serialized = Toml.serializeFrom(arrayTest);
|
||||||
|
String expected = "array = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]\n";
|
||||||
|
assertEquals(expected, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializesList() {
|
||||||
|
class ListTest {
|
||||||
|
List<Integer> aList = new LinkedList<Integer>();
|
||||||
|
}
|
||||||
|
ListTest o = new ListTest();
|
||||||
|
o.aList.add(1);
|
||||||
|
o.aList.add(2);
|
||||||
|
|
||||||
|
assertEquals("aList = [ 1, 2 ]\n", Toml.serializeFrom(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handlesZeroLengthArraysAndLists() {
|
||||||
|
class TestClass {
|
||||||
|
List<Integer> aList = new LinkedList<Integer>();
|
||||||
|
Float[] anArray = new Float[0];
|
||||||
|
}
|
||||||
|
assertEquals("", Toml.serializeFrom(new TestClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elidesEmptyIntermediateTables() {
|
||||||
|
class C {
|
||||||
|
int anInt = 1;
|
||||||
|
}
|
||||||
|
class B {
|
||||||
|
C c = new C();
|
||||||
|
}
|
||||||
|
class A {
|
||||||
|
B b = new B();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("[b.c]\n anInt = 1\n", Toml.serializeFrom(new A()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializesNestedArraysOfTables() {
|
||||||
|
class Physical {
|
||||||
|
String color;
|
||||||
|
String shape;
|
||||||
|
}
|
||||||
|
class Variety {
|
||||||
|
String name;
|
||||||
|
}
|
||||||
|
class Fruit {
|
||||||
|
Physical physical;
|
||||||
|
Variety[] variety;
|
||||||
|
String name;
|
||||||
|
}
|
||||||
|
class Basket {
|
||||||
|
Fruit[] fruit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Basket basket = new Basket();
|
||||||
|
basket.fruit = new Fruit[2];
|
||||||
|
|
||||||
|
basket.fruit[0] = new Fruit();
|
||||||
|
basket.fruit[0].name = "apple";
|
||||||
|
basket.fruit[0].physical = new Physical();
|
||||||
|
basket.fruit[0].physical.color = "red";
|
||||||
|
basket.fruit[0].physical.shape = "round";
|
||||||
|
basket.fruit[0].variety = new Variety[2];
|
||||||
|
basket.fruit[0].variety[0] = new Variety();
|
||||||
|
basket.fruit[0].variety[0].name = "red delicious";
|
||||||
|
basket.fruit[0].variety[1] = new Variety();
|
||||||
|
basket.fruit[0].variety[1].name = "granny smith";
|
||||||
|
|
||||||
|
basket.fruit[1] = new Fruit();
|
||||||
|
basket.fruit[1].name = "banana";
|
||||||
|
basket.fruit[1].variety = new Variety[1];
|
||||||
|
basket.fruit[1].variety[0] = new Variety();
|
||||||
|
basket.fruit[1].variety[0].name = "plantain";
|
||||||
|
|
||||||
|
String expected = "[[fruit]]\n" +
|
||||||
|
" name = \"apple\"\n" +
|
||||||
|
"\n" +
|
||||||
|
"[fruit.physical]\n" +
|
||||||
|
" color = \"red\"\n" +
|
||||||
|
" shape = \"round\"\n" +
|
||||||
|
"\n" +
|
||||||
|
"[[fruit.variety]]\n" +
|
||||||
|
" name = \"red delicious\"\n" +
|
||||||
|
"\n" +
|
||||||
|
"[[fruit.variety]]\n" +
|
||||||
|
" name = \"granny smith\"\n" +
|
||||||
|
"\n" +
|
||||||
|
"[[fruit]]\n" +
|
||||||
|
" name = \"banana\"\n" +
|
||||||
|
"\n" +
|
||||||
|
"[[fruit.variety]]\n" +
|
||||||
|
" name = \"plantain\"" +
|
||||||
|
"\n";
|
||||||
|
|
||||||
|
|
||||||
|
String serialized = Toml.serializeFrom(basket);
|
||||||
|
assertEquals(expected, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializesClassesWithInheritance() {
|
||||||
|
class Parent {
|
||||||
|
protected int anInt = 2;
|
||||||
|
}
|
||||||
|
class Child extends Parent {
|
||||||
|
boolean aBoolean = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Child child = new Child();
|
||||||
|
String expected = "aBoolean = true\nanInt = 2\n";
|
||||||
|
assertEquals(expected, Toml.serializeFrom(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void emptyTomlSerializesToEmptyString() {
|
||||||
|
Toml toml = new Toml();
|
||||||
|
assertEquals("", toml.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializesStringsToTomlUtf8() throws UnsupportedEncodingException {
|
||||||
|
String input = " é foo € \b \t \n \f \r \" \\ ";
|
||||||
|
assertEquals("\" \\u00E9 foo \\u20AC \\b \\t \\n \\f \\r \\\" \\ \"", Toml.serializeFrom(input));
|
||||||
|
|
||||||
|
// Check unicode code points greater than 0XFFFF
|
||||||
|
input = " \uD801\uDC28 \uD840\uDC0B ";
|
||||||
|
assertEquals("\" \\U00010428 \\U0002000B \"", Toml.serializeFrom(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void quotesKeys() {
|
||||||
|
Map<String, Integer> aMap = new LinkedHashMap<String, Integer>();
|
||||||
|
aMap.put("a.b", 1);
|
||||||
|
aMap.put("5€", 2);
|
||||||
|
aMap.put("c$d", 3);
|
||||||
|
aMap.put("e/f", 4);
|
||||||
|
|
||||||
|
String expected = "\"a.b\" = 1\n" +
|
||||||
|
"\"5€\" = 2\n" +
|
||||||
|
"\"c$d\" = 3\n" +
|
||||||
|
"\"e/f\" = 4\n";
|
||||||
|
assertEquals(expected, Toml.serializeFrom(aMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializesFromToml() {
|
||||||
|
String tomlString = "a = 1\n";
|
||||||
|
Toml toml = new Toml().parse(tomlString);
|
||||||
|
assertEquals(tomlString, toml.serialize());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue