我有一个程序可以将数据保存/加载到文本文件中。但是,有些数据似乎没有保存,我无法弄清楚原因。
这是代码的简化版本。
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
public class Test {
private static DumperOptions options;
private static File saveLocation;
private static Map<String, Object> data;
private static Representer representer;
private static String path;
private static Yaml yaml;
private static void setup() {
saveLocation = new File(path);
if (!saveLocation.exists())
try {
saveLocation.createNewFile();
} catch (IOException exception) {
exception.printStackTrace();
}
setupDumper();
yaml = new Yaml(representer, options);
data = Collections.synchronizedMap(new LinkedHashMap<String, Object>());
}
private static void setupDumper() {
options = new DumperOptions();
representer = new Representer();
options.setIndent(2);
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setAllowUnicode(Charset.defaultCharset().name().contains("UTF"));
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
}
public static boolean contains(String key) {
return data.containsKey(key);
}
public static boolean exists() {
return data != null;
}
public static boolean hasValue(String key) {
Object tempObject = data.get(key);
return (tempObject != null);
}
public static boolean isEmpty() {
return data.isEmpty() || data == null;
}
public static int getInteger(String key) {
Object tempObject = get(key);
if (tempObject instanceof Integer)
return (Integer) tempObject;
if (tempObject instanceof String)
return Integer.parseInt(tempObject.toString());
if (tempObject instanceof Number)
return Integer.parseInt(tempObject.toString());
return -1;
}
public static Object get(String key) {
if (isEmpty())
return null;
if (key.contains(".")) {
String[] nodes = key.split("\\.");
Map<String, Object> currParent;
if (data.containsKey(nodes[0]) && (data.get(nodes[0]) instanceof Map))
currParent = (Map) data.get(nodes[0]);
else return null;
if (nodes.length > 1) {
for (int i = 1; i < nodes.length - 1; i++) {
if (currParent.containsKey(nodes[i]) && (currParent.get(nodes[i]) instanceof Map))
currParent = (Map) currParent.get(nodes[i]);
else return null;
}
if (currParent.containsKey(nodes[nodes.length - 1]))
return currParent.get(nodes[nodes.length - 1]);
}
} else if (!contains(key) || (contains(key) && !hasValue(key)))
return null;
return data.get(key);
}
public static FileReader read(File file) {
try {
if (!file.exists())
return null;
return new FileReader(file);
} catch (FileNotFoundException exception) {
exception.printStackTrace();
}
return null;
}
public static boolean load() {
setup();
FileReader reader = read(saveLocation);
if (reader == null) {
data = Collections.synchronizedMap(new LinkedHashMap<String, Object>());
return true;
}
data = Collections.synchronizedMap((Map<String, Object>) yaml.load(reader));
System.out.println(getInteger("Settings.Difficulty.Level"));
System.out.println(getInteger("Settings.Difficulty.Locked"));
System.out.println(getInteger("Settings.Cheats.Enabled"));
System.out.println(getInteger("Settings.Cheats.Locked"));
System.out.println(getInteger("Settings.GUI.Enabled"));
System.out.println(data.toString());
return true;
}
public static void save() {
//Settings
set("Settings.Difficulty.Level", 1);// not saving
set("Settings.Difficulty.Locked", 2);
set("Settings.Cheats.Enabled", 3); // not saving
set("Settings.Cheats.Locked", 4);
set("Settings.GUI.Enabled", 5);
try {
if (!saveLocation.exists())
saveLocation.createNewFile();
FileWriter writer = new FileWriter(saveLocation);
writer.write(yaml.dump(data));
writer.flush();
writer.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
public static void set(String key, Object object) {
if (!exists())
return;
if (key.contains(".")) {
String[] nodes = key.split("\\.");
// if data doesn't contain top-level node, create nested Maps
if (!data.containsKey(nodes[0])) {
Map<String, Object> currNode = new HashMap<>(), prevNode;
currNode.put(nodes[nodes.length - 1], object);
for (int i = nodes.length - 2; i > 0; i--) {
prevNode = currNode;
currNode = new HashMap<>();
currNode.put(nodes[i], prevNode);
}
// set top-level node to previous parent
data.put(nodes[0], currNode);
} else { // if data contains top-level node, work through each Map
Map[] prevNodes = new LinkedHashMap[nodes.length - 1];
if (nodes.length > 1) {
for (int i = 0; i <= nodes.length - 2; i++) {
if (data.containsKey(nodes[i]) && (data.get(nodes[i]) instanceof Map))
prevNodes[i] = new LinkedHashMap((Map) data.get(nodes[i]));
else if (!data.containsKey(nodes[i]))
prevNodes[i] = new LinkedHashMap();
}
prevNodes[prevNodes.length - 1].put(nodes[nodes.length - 1], object);
for (int i = prevNodes.length - 1; i >= 1; i--)
prevNodes[i - 1].put(nodes[i], prevNodes[i]);
data.put(nodes[0], prevNodes[0]);
} else data.put(nodes[0], object);
}
return;
}
data.put(key, object);
}
public static void main(String[] args) {
path = "test.txt";
setup();
save();
load();
}
}
通常它会从一堆其他类中保存整数,然后当程序再次运行时,将其加载回适当的变量。但是为了测试,它只是打印出值。
//Settings
set("Settings.Difficulty.Level", 1);// not saving
set("Settings.Difficulty.Locked", 2);
set("Settings.Cheats.Enabled", 3); // not saving
set("Settings.Cheats.Locked", 4);
set("Settings.GUI.Enabled", 5);
这应该将整数(1到5)保存到test.txt,然后将它们打印到控制台
System.out.println(getInteger("Settings.Difficulty.Level"));
System.out.println(getInteger("Settings.Difficulty.Locked"));
System.out.println(getInteger("Settings.Cheats.Enabled"));
System.out.println(getInteger("Settings.Cheats.Locked"));
System.out.println(getInteger("Settings.GUI.Enabled"));
当我运行文件时,我希望得到
的输出1
2
3
4
5
但实际上我得到了
-1
2
-1
4
5
Settings.Difficulty.Level
和Settings.Cheats.Enabled
似乎根本没有保存在文件中。
Settings:
Difficulty:
Locked: 2
Cheats:
Locked: 4
GUI:
Enabled: 5
我很确定问题出在set
方法的某个地方,但我不确定它究竟是什么。我也不知道为什么5个变量中只有两个不能保存(必须与使用的名称有关)。
答案 0 :(得分:3)
错误在于:
for (int i = 0; i <= nodes.length - 2; i++) {
if (data.containsKey(nodes[i]) && (data.get(nodes[i]) instanceof Map))
prevNodes[i] = new LinkedHashMap((Map) data.get(nodes[i]));
else if (!data.containsKey(nodes[i]))
prevNodes[i] = new LinkedHashMap();
}
此代码在第二次set
调用时执行。它在"Settings"
中找到了data
,但是,它在"Difficulty"
中找不到data
(因为它位于"Settings"
地图中)。所以它创建了一个新的空LinkedHashMap
。然后,这段代码发生了:
prevNodes[prevNodes.length - 1].put(nodes[nodes.length - 1], object);
for (int i = prevNodes.length - 1; i >= 1; i--)
prevNodes[i - 1].put(nodes[i], prevNodes[i]);
它将现有的"Difficulty"
节点(包含"Level"
)替换为新创建的节点(仅包含"Locked"
)。因此,"Level"
之后就失踪了。
您可以像这样修改for
循环:
for (int i = 0; i <= nodes.length - 2; i++) {
final Map cur = (i == 0) ? data : prevNodes[i - 1];
if (cur.containsKey(nodes[i]) && (cur.get(nodes[i]) instanceof Map))
prevNodes[i] = new LinkedHashMap((Map) cur.get(nodes[i]));
else if (!cur.containsKey(nodes[i]))
prevNodes[i] = new LinkedHashMap();
}
但实际上,整个set
方法可以简化:(注意,这是未经测试的代码,可能包含小错误)
public static void set(String key, Object object) {
if (!exists())
return;
// no need to differentiate between paths with and without "."
final String[] nodes = key.split("\\.");
Map cur = data;
for (int i = 0; i <= nodes.length - 2; ++i) {
Object val = cur.get(nodes[i]);
if (val == null) {
val = new LinkedHashMap();
cur.put(nodes[i], val);
} else if (!(val instanceof Map)) {
// error in structure, handle it here (throw exception, …)
}
cur = (Map) val;
}
cur.put(nodes[nodes.length - 1], object);
}