|
|
|
|
@@ -5,8 +5,11 @@ import java.io.FileWriter;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.lang.reflect.Field;
|
|
|
|
|
import java.lang.reflect.ParameterizedType;
|
|
|
|
|
import java.nio.file.Files;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
|
import java.util.function.Function;
|
|
|
|
|
import java.util.regex.Matcher;
|
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
NightJson v0.2 by Martin Prokoph
|
|
|
|
|
@@ -14,7 +17,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
|
Concept inspired by GSON
|
|
|
|
|
*/
|
|
|
|
|
public class NightJson {
|
|
|
|
|
private static final String KEY_PATTERN = "\"(-?.*)\":";
|
|
|
|
|
private static final String KEY_PATTERN = "\"(-?[A-Za-z-_.]*)\":";
|
|
|
|
|
Class<?> jsonClass;
|
|
|
|
|
Field jsonMap;
|
|
|
|
|
String fileLocation;
|
|
|
|
|
@@ -28,6 +31,7 @@ public class NightJson {
|
|
|
|
|
} catch (NoSuchFieldException ignored) {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
public void writeJson() {
|
|
|
|
|
if (fileLocation == null) return;
|
|
|
|
|
try {
|
|
|
|
|
@@ -39,38 +43,37 @@ public class NightJson {
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
Field field = it.next();
|
|
|
|
|
if (field == jsonMap) continue;
|
|
|
|
|
writeElement(jsonFile, field.get(null), field.getType(), field.getName());
|
|
|
|
|
jsonFile.write(it.hasNext() ? ",\n" : "\n");
|
|
|
|
|
writeElement(jsonFile, field.get(null), field.getType(), field.getName(), it.hasNext());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (jsonMap != null) {
|
|
|
|
|
//noinspection unchecked
|
|
|
|
|
Iterator<String> it = ((Map<String,?>)jsonMap.get(null)).keySet().iterator();
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
String key = it.next();
|
|
|
|
|
Object value = jsonMap.get(key);
|
|
|
|
|
writeElement(jsonFile, jsonMap.get(key), value.getClass(), key);
|
|
|
|
|
jsonFile.write(it.hasNext() ? ",\n" : "\n");
|
|
|
|
|
writeElement(jsonFile, jsonMap.get(key), value.getClass(), key, it.hasNext());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
jsonFile.write("}");
|
|
|
|
|
jsonFile.close();
|
|
|
|
|
} catch (IOException | IllegalAccessException e) {
|
|
|
|
|
System.out.println("Oh no! An Error occurred whilst writing the JSON file :(");
|
|
|
|
|
e.fillInStackTrace();
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void writeElement(FileWriter jsonFile, Object value, Class<?> type, String name) throws IOException, IllegalAccessException {
|
|
|
|
|
private void writeElement(FileWriter jsonFile, Object value, Class<?> type, String name, boolean hasNext) throws IOException, IllegalAccessException {
|
|
|
|
|
jsonFile.write("\t");
|
|
|
|
|
if (type == Comment.class) {
|
|
|
|
|
jsonFile.write(String.format("// %s\n", ((Comment) value).commentString));
|
|
|
|
|
jsonFile.write(String.format("\n\t// %s\n", ((Comment) value).commentString));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
jsonFile.write(String.format("\"%s\": ", name));
|
|
|
|
|
jsonFile.write(objToString(value, type));
|
|
|
|
|
jsonFile.write(hasNext ? ",\n" : "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
public void readJson() {
|
|
|
|
|
if (fileLocation == null) return;
|
|
|
|
|
try {
|
|
|
|
|
@@ -80,49 +83,69 @@ public class NightJson {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Scanner jsonFile = new Scanner(file);
|
|
|
|
|
Map<String, String> jsonKeyValuePairs = new HashMap<>();
|
|
|
|
|
AtomicReference<String> lastKey = new AtomicReference<>();
|
|
|
|
|
jsonFile.forEachRemaining(s -> {
|
|
|
|
|
if (!s.matches("[{}]") && !s.matches("//+")) {
|
|
|
|
|
if (s.matches(KEY_PATTERN)) {
|
|
|
|
|
lastKey.set(s.replaceAll("([\":])", ""));
|
|
|
|
|
jsonKeyValuePairs.put(lastKey.get(), "");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
String val = s.replaceAll("(\")", "");
|
|
|
|
|
if (val.endsWith(",")) val = val.substring(0, val.length()-1);
|
|
|
|
|
jsonKeyValuePairs.put(lastKey.get(), (jsonKeyValuePairs.get(
|
|
|
|
|
lastKey.get()).isEmpty() ? "" : jsonKeyValuePairs.get(lastKey.get()) + " "
|
|
|
|
|
) + val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
Map<String, Object> asMap = jsonToMap(Files.readString(file.toPath()).replaceAll("(//)+.*\n", ""), (key) -> getField(key).isPresent() ? getField(key).get().getType() : String.class);
|
|
|
|
|
|
|
|
|
|
for (String key : jsonKeyValuePairs.keySet()) {
|
|
|
|
|
String currentString = jsonKeyValuePairs.get(key);
|
|
|
|
|
//System.out.printf("Key: %s Value: %s%n", key, currentString);
|
|
|
|
|
boolean isInClass = false;
|
|
|
|
|
if (jsonClass != null) {
|
|
|
|
|
Field field;
|
|
|
|
|
try {
|
|
|
|
|
field = jsonClass.getField(key);
|
|
|
|
|
field.set(field, stringToFieldObj(currentString, field));
|
|
|
|
|
isInClass = true;
|
|
|
|
|
} catch (NoSuchFieldException ignored) {}
|
|
|
|
|
for (String key : asMap.keySet()) {
|
|
|
|
|
Object value = asMap.get(key);
|
|
|
|
|
Optional<Field> field = getField(key);
|
|
|
|
|
if (field.isPresent()) {
|
|
|
|
|
field.get().set(null, value);
|
|
|
|
|
}
|
|
|
|
|
if (jsonMap != null && !isInClass) {
|
|
|
|
|
//noinspection unchecked
|
|
|
|
|
((Map<String,Object>)jsonMap.get(null)).put(key, stringToObj(currentString, getTypeArgument(jsonMap, 1)));
|
|
|
|
|
else if (jsonMap != null) {
|
|
|
|
|
((Map<String, Object>)jsonMap.get(null)).put(key, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
jsonFile.close();
|
|
|
|
|
} catch (IOException | IllegalAccessException | NoSuchElementException e) {
|
|
|
|
|
} catch (IOException | IllegalAccessException | NoSuchElementException | ClassCastException e) {
|
|
|
|
|
System.out.println("Oh no! An Error occurred whilst reading the JSON file :(");
|
|
|
|
|
e.fillInStackTrace();
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Map<String, Object> jsonToMap(String jsonString, Function<String, Class<?>> keyToType) {
|
|
|
|
|
Map<String, Object> map = new HashMap<>();
|
|
|
|
|
Iterator<String> pairIterator = Arrays.stream(jsonString.replaceAll("(//)+.*\n", "").replaceFirst("[{]", "").split(",")).iterator();
|
|
|
|
|
while (pairIterator.hasNext()) {
|
|
|
|
|
String s = pairIterator.next();
|
|
|
|
|
|
|
|
|
|
Matcher matcher = Pattern.compile(KEY_PATTERN).matcher(s);
|
|
|
|
|
if (matcher.find()) {
|
|
|
|
|
String key = matcher.group().replaceAll("([\":])", "");
|
|
|
|
|
String val = s.split(KEY_PATTERN, 2)[1];
|
|
|
|
|
|
|
|
|
|
StringBuilder submapString = new StringBuilder();
|
|
|
|
|
if (s.contains("{")) {
|
|
|
|
|
int level = charAmount(s, '{');
|
|
|
|
|
submapString.append(val);
|
|
|
|
|
if (pairIterator.hasNext()) submapString.append(",");
|
|
|
|
|
while (pairIterator.hasNext()) {
|
|
|
|
|
String next = pairIterator.next();
|
|
|
|
|
submapString.append(next);
|
|
|
|
|
if (next.contains("{")) level += charAmount(next, '{');
|
|
|
|
|
if (next.contains("}")) level -= charAmount(next, '}');
|
|
|
|
|
if (level <= 0) break;
|
|
|
|
|
if (pairIterator.hasNext()) submapString.append(",");
|
|
|
|
|
}
|
|
|
|
|
System.out.println(submapString);
|
|
|
|
|
}
|
|
|
|
|
if (submapString.length() > 0) {
|
|
|
|
|
Optional<Field> field = getField(key);
|
|
|
|
|
map.put(key, jsonToMap(String.valueOf(submapString), k -> field.isPresent() ? getTypeArgument(field.get(), 1) : String.class));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (val.startsWith(" ")) val = val.substring(1);
|
|
|
|
|
val = val.replaceAll("[\"}\n]", "");
|
|
|
|
|
if (val.endsWith(",")) val = val.substring(0, val.length() - 1);
|
|
|
|
|
|
|
|
|
|
map.put(key, stringToObj(val, keyToType.apply(key)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return map;
|
|
|
|
|
}
|
|
|
|
|
private int charAmount(String input, char c) {
|
|
|
|
|
return (int) input.chars().filter(ch -> ch == c).count();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String objToString(Object value, Class<?> type) {
|
|
|
|
|
if (type == Map.class) {
|
|
|
|
|
StringBuilder mapPairs = new StringBuilder();
|
|
|
|
|
@@ -141,36 +164,18 @@ public class NightJson {
|
|
|
|
|
return String.format(type == String.class || type.isEnum() ? "\"%s\"" : "%s", value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Object stringToFieldObj(String currentString, Field field) {
|
|
|
|
|
if (field.getType() == Map.class) {
|
|
|
|
|
Map<Object, Object> map = new HashMap<>();
|
|
|
|
|
Iterator<String> it = Arrays.stream(currentString.substring(1, currentString.length()-1).split(",")).iterator();
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
String pair = it.next();
|
|
|
|
|
if (!pair.contains(":")) break;
|
|
|
|
|
int semicolonPos = pair.indexOf(":");
|
|
|
|
|
Class<?> keyType = getTypeArgument(field, 0);
|
|
|
|
|
Class<?> valType = getTypeArgument(field, 1);
|
|
|
|
|
map.put(stringToObj(pair.substring(0, semicolonPos), keyType),
|
|
|
|
|
stringToObj(pair.substring(semicolonPos+2), valType));
|
|
|
|
|
}
|
|
|
|
|
return map;
|
|
|
|
|
}
|
|
|
|
|
return stringToObj(currentString, field.getType());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Object stringToObj(String currentString, Class<?> type) {
|
|
|
|
|
private Object stringToObj(String value, Class<?> type) {
|
|
|
|
|
switch (type.getName()) {
|
|
|
|
|
case "byte": return Byte.parseByte(currentString);
|
|
|
|
|
case "int": return Integer.parseInt(currentString);
|
|
|
|
|
case "long": return Long.parseLong(currentString);
|
|
|
|
|
case "float": return Float.parseFloat(currentString);
|
|
|
|
|
case "double": return Double.parseDouble(currentString);
|
|
|
|
|
case "boolean": return Boolean.parseBoolean(currentString);
|
|
|
|
|
case "byte": return Byte.parseByte(value);
|
|
|
|
|
case "int": return Integer.parseInt(value);
|
|
|
|
|
case "long": return Long.parseLong(value);
|
|
|
|
|
case "float": return Float.parseFloat(value);
|
|
|
|
|
case "double": return Double.parseDouble(value);
|
|
|
|
|
case "boolean": return Boolean.parseBoolean(value);
|
|
|
|
|
}
|
|
|
|
|
if (type.isEnum()) return Arrays.stream(type.getEnumConstants())
|
|
|
|
|
.filter(enumConstant -> Objects.equals(enumConstant.toString(), currentString)).findFirst().orElseThrow();
|
|
|
|
|
else return currentString;
|
|
|
|
|
.filter(enumConstant -> Objects.equals(enumConstant.toString(), value)).findFirst().orElseThrow();
|
|
|
|
|
else return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Class<?> getTypeArgument(Field field, int index) {
|
|
|
|
|
@@ -182,10 +187,18 @@ public class NightJson {
|
|
|
|
|
} catch (NoSuchFieldException | IllegalAccessException ignored) { return rawType; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Optional<Field> getField(String name) {
|
|
|
|
|
try {
|
|
|
|
|
return Optional.of(jsonClass.getField(name));
|
|
|
|
|
} catch (NoSuchFieldException e) {
|
|
|
|
|
return Optional.empty();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class Comment {
|
|
|
|
|
final String commentString;
|
|
|
|
|
public Comment(String commentString) {
|
|
|
|
|
this.commentString = commentString;
|
|
|
|
|
public Comment(String commentString, Object... args) {
|
|
|
|
|
this.commentString = String.format(commentString, args);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|