# 配置API的序列化和遍历
# 序列化
# 了解序列化
如果我自己做了一个类型, 例如下面的Person类:
public class Person {
public String name;
public String introduction;
public Person(String name, String introduction) {
this.name = name;
this.introduction = introduction;
}
}
现在我们新建一个Person对象:
Person person = new Person("tdiant", "hello!!");
我们想把Person保存在配置文件里怎么办?
很遗憾,直接getConfig().set("demo",person);是行不通的. 你会发现getConfig.get("demo")根本得不到这个对象.
哪些东西可以直接set保存呢? 类似getInt, 所有拥有get方法的类型都可以直接保存. (包括
List<String>)还有一些BukkitAPI给的类型, 例如ItemStack. 但不是全部都是这样.
如果你想判断一个类型是不是可以直接set, 你可以在JavaDoc中找到它, 看它是否实现了ConfigurationSerializable类.
你可能想到了最简单粗暴的办法:
//这样set
getConfig().set("demo.name",test.name);
getConfig().set("demo.introduction",test.introduction);
//然后保存, 用的时候这样
getConfig().getString("demo.name");
getConfig().getString("demo.introduction");
这的确是一种切实可行的办法. 但是这真的是太麻烦了. 有没有一种方法直接set或get这个对象的办法呢? 有! 你可以使用序列化和反序列化实现它!
# 让自定义类型实现序列化与反序列化
以上文Person为例. 首先让他实现ConfigurationSerializable, 并添加deserialize方法. 如下:
public class Person implements ConfigurationSerializable {
public String name;
public String introduction;
public Person(String name, String introduction) {
this.name = name;
this.introduction = introduction;
}
@Override
public Map<String, Object> serialize() {
Map<String, Object> map = new HashMap<>();
return map;
}
public static Person deserialize(Map<String, Object> map) {
}
}
然后继续完善serialize, 实现序列化. 我们只需要把需要保存的数据写入map当中即可.
注意, 需要保存的数据要保证可以直接set, 不能则也需要为他实现序列化与反序列化.
@Override
public Map<String,Object> serialize() {
Map<String,Object> map = new HashMap<>();
map.put("name",name);
map.put("introduction",introduction);
return map;
}
序列化后, 数据即可直接set进配置文件里. 为了实现直接get的目的, 还需要进行反序列化.
public static Person deserialize(Map<String, Object> map) {
return new Person(
(map.get("name") != null ? (String) map.get("name") : ""),
(map.get("introduction") != null ? (String) map.get("introduction") : "")
);
}
编写完毕后, 我们需要像注册监听器一样, 注册序列化. 在插件主类的onEnable中加入如下语句:
ConfigurationSerialization.registerClass(Person.class);
为什么要这么做呢? 可以找到 ConfigurationSerializable的JavaDocs页面

至此, 你就可以自由地对一个自定义的对象直接地get和set了!
下面分别演示set与get:
// set
Person person = new Person("tdiant", "hello!!");
getConfig().set("demo", person);
saveConfig();
默认配置文件(config.yml)将出现:
demo:
==: myplugin.Person
name: tdiant
introduction: hello!!
BukkitAPI会根据==的值判断这段配置是由什么类序列化而来的, 进而方便其反序列化.
不要轻易改动==属性的值, 否则BukkitAPI会因为找不到类或此类没有被注册(ConfigurationSerialization类的registerClass方法)而报错.
// get
Person person = (Person) getConfig().get("demo");
System.out.println("name = " + person.name + " introduction = " + person.introduction);
控制台将输出:
name = tdiant introduction = hello!!
# 配置文件的遍历
试想, 如果存在下面的配置文件:
demo_list:
a: 1
b: 233
c: 666
d: lalalalalal
我应该如何对demo_list的子键进行遍历, 得到所有子键的对应值?
最简单错报的方式就是将demo_list.a键、demo_list.b键...依次读取. 但这是建立在你知道demo_list有a、b、c...这些子键的基础之上的.
如果我事先不知道demo_list的子键都各自叫什么, 又应该如何对demo_list的子键进行遍历, 得到所有子键的对应值?
# 配置片段 ConfigurationSection
我们可以把demo_list键对应的部分拆出来.
下文假设config对象是我们现在正在操作的FileConfiguration对象.
ConfigurationSection cs = config.getConfigurationSection("demo_list");
这里我们得到了ConfigurationSection对象, 这个对象可以当做config对象demo_list键部分的片段, 等效于这个yaml数据:
a: 1
b: 233
c: 666
d: lalalalalal
对于一个ConfigurationSection对象, 其代表着一个完整配置数据的某个片段, 你不能直接利用诸如saveConfig的方式保存这个片段到另外一个yml文件里.
# 利用getKeys方法实现遍历
在上面我们得到了ConfigurationSection对象, 这代表着config对象demo_list键部分的片段.
现在问题转化成了, 如何获取到ConfigurationSection对象里的所有键.
可以利用getKeys(false)的方式达到目的.
for(String key : cs.getKeys(false)) {
System.out.println(key + " = " + cs.get(key));
}
上面的代码将输出:
a = 1
b = 233
c = 666
d = lalalalalal
这样就实现了遍历.
getKeys方法不只是ConfigurationSection拥有, 根据其继承关系, 我们可以推知对FileConfiguration类也拥有getKeys方法, 同理, ConfigurationSection类也有getConfigurationSection方法.
但是我们刚才为什么要给getKeys的一个false的参数呢? 请看下面的yaml数据:
test:
a:
b: 1
c: 2
d: 1
我们得到了这个配置文件的FileConfiguration对象config, 现在对其用getKeys(false)进行遍历, 得到所有键.
for(String key : config.getKeys(false)) {
System.out.println(key);
}
System.out.println("===================");
for(String key : config.getKeys(true)) {
System.out.println(key);
}
输出结果如下:
test
d
===================
test
test.a
test.a.b
test.c
d
由此可知, getKeys(false)只能获取“一层的键”, 不能递归获取配置文件里所有的键. 而getKeys(true)会递归获取配置文件里所有出现的键.
进阶
30分钟