Java不保存所有数据

时间:2016-08-14 21:12:02

标签: java yaml

我有一个程序可以将数据保存/加载到文本文件中。但是,有些数据似乎没有保存,我无法弄清楚原因。

这是代码的简化版本。

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.LevelSettings.Cheats.Enabled似乎根本没有保存在文件中。

Settings:
  Difficulty:
    Locked: 2
  Cheats:
    Locked: 4
  GUI:
    Enabled: 5

我很确定问题出在set方法的某个地方,但我不确定它究竟是什么。我也不知道为什么5个变量中只有两个不能保存(必须与使用的名称有关)。

1 个答案:

答案 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);
}