使用 Gson 序列化和反序列化 org.bukkit.ItemStack
写这玩意的原因
昨天肝了一整天 这个插件,为了方便起见我希望使用 Json 来存储 ItemStack 信息,结果没想到为了序列化这个 ItemStack 花了我一整个下午的时间。在 StackOverFlow 和 SpigotMC 兜兜转转一大圈后,终于写出来了序列化代码。
正好刚刚逛论坛的时候,看到了同小组的 这个教程,心想正好他只写了 YAML,没有写 GSON,我就帮忙给他补充一下了233。
开始
探寻 ItemStack 序列化的实质
既然 Bukkit API 已经向我们提供了 ItemStack 的序列化和反序列化方法,那么就让我们深入一下这两个方法:
复制代码复制代码
由此看来,就非常明了了:原来 ItemStack 的序列化就是将各种属性存储到一个 Map<String, Object> 里,那么我们只需要将这个 Map<String,Object> 通过 Gson 序列化为 Json,就解决问题啦!
配置 Gson 并自定义 Gson 序列化器
默认情况下,Gson 并不会调用 ItemStack 的序列化和反序列化方法,如果不调用这些方法而强行序列化,就会引发奇怪的报错。因此我们需要自定义 Gson 序列化器。因此,创建 ItemStackSerializer,并实现 JsonDeserializer<ItemStack>, JsonSerializer<ItemStack>:
复制代码
然后,使用 GsonBuilder 生成一个注册了 ItemStack 序列化器的 Gson 对象:
复制代码
这样一来,我们就可以使用 gson.fromJson 或是 gson.toJson 将 ItemStack 正确的序列化或是反序列化啦!
来自群组: Server CT
写这玩意的原因
昨天肝了一整天 这个插件,为了方便起见我希望使用 Json 来存储 ItemStack 信息,结果没想到为了序列化这个 ItemStack 花了我一整个下午的时间。在 StackOverFlow 和 SpigotMC 兜兜转转一大圈后,终于写出来了序列化代码。
正好刚刚逛论坛的时候,看到了同小组的 这个教程,心想正好他只写了 YAML,没有写 GSON,我就帮忙给他补充一下了233。
开始
探寻 ItemStack 序列化的实质
既然 Bukkit API 已经向我们提供了 ItemStack 的序列化和反序列化方法,那么就让我们深入一下这两个方法:
- // on ItemStack.class
- @Utility
- public Map<String, Object> serialize() {
- Map<String, Object> result = new LinkedHashMap<String, Object>();
- result.put("type", getType().name());
- if (getDurability() != 0) {
- result.put("damage", getDurability());
- }
- if (getAmount() != 1) {
- result.put("amount", getAmount());
- }
- ItemMeta meta = getItemMeta();
- if (!Bukkit.getItemFactory().equals(meta, null)) {
- result.put("meta", meta);
- }
- return result;
- }
- // on ItemStack.class
- public static ItemStack deserialize(Map<String, Object> args) {
- Material type = Material.getMaterial((String) args.get("type"));
- short damage = 0;
- int amount = 1;
- if (args.containsKey("damage")) {
- damage = ((Number) args.get("damage")).shortValue();
- }
- if (args.containsKey("amount")) {
- amount = ((Number) args.get("amount")).intValue();
- }
- ItemStack result = new ItemStack(type, amount, damage);
- if (args.containsKey("enchantments")) { // Backward compatiblity, @deprecated
- Object raw = args.get("enchantments");
- if (raw instanceof Map) {
- Map<?, ?> map = (Map<?, ?>) raw;
- for (Map.Entry<?, ?> entry : map.entrySet()) {
- Enchantment enchantment = Enchantment.getByName(entry.getKey().toString());
- if ((enchantment != null) && (entry.getValue() instanceof Integer)) {
- result.addUnsafeEnchantment(enchantment, (Integer) entry.getValue());
- }
- }
- }
- } else if (args.containsKey("meta")) { // We cannot and will not have meta when enchantments (pre-ItemMeta) exist
- Object raw = args.get("meta");
- if (raw instanceof ItemMeta) {
- result.setItemMeta((ItemMeta) raw);
- }
- }
- return result;
- }
由此看来,就非常明了了:原来 ItemStack 的序列化就是将各种属性存储到一个 Map<String, Object> 里,那么我们只需要将这个 Map<String,Object> 通过 Gson 序列化为 Json,就解决问题啦!
配置 Gson 并自定义 Gson 序列化器
默认情况下,Gson 并不会调用 ItemStack 的序列化和反序列化方法,如果不调用这些方法而强行序列化,就会引发奇怪的报错。因此我们需要自定义 Gson 序列化器。因此,创建 ItemStackSerializer,并实现 JsonDeserializer<ItemStack>, JsonSerializer<ItemStack>:
- public class ItemStackSerializer implements JsonDeserializer<ItemStack>, JsonSerializer<ItemStack> {
- @Override
- public ItemStack deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
- return ItemStack.deserialize(GsonBuilder().create().fromJson(json, new TypeToken<Map<String, Object>>() {}.getType()));
- }
- @Override
- public JsonElement serialize(ItemStack src, Type typeOfSrc, JsonSerializationContext context) {
- return new GsonBuilder().create().toJsonTree(src.getItem().serialize());
- }
- }
然后,使用 GsonBuilder 生成一个注册了 ItemStack 序列化器的 Gson 对象:
- Gson gson = new GsonBuilder()
- .enableComplexMapKeySerialization()
- .serializeNulls()
- .setPrettyPrinting()
- .registerTypeAdapter(ItemStack.class, new ItemStackSerializer())
- .create();
这样一来,我们就可以使用 gson.fromJson 或是 gson.toJson 将 ItemStack 正确的序列化或是反序列化啦!
来自群组: Server CT
荷兰好快荷兰好快荷兰好快荷兰好快荷兰好快荷兰好快
萌新请问,这种方式对nbt属性也可以成功保存吗
金瓯 发表于 2021-8-8 12:05
萌新请问,这种方式对nbt属性也可以成功保存吗
不能,,
不完全对,实际上 ItemMeta 的本质就是 NBT,这种 BukkitAPI 提供的 NBT 实现是可以被序列化的
但自定义的 NBT 就不行了
(没有测试过高版本的持久化序列接口可否被序列化,个人猜测应该也是可以的)
想问问为什么会出现这种错误https://paste.ubuntu.com/p/GhGcxs4Qbc/
复制代码
服务端paper1.16.5 git-465
- class ItemStackSerializer : JsonSerializer<ItemStack>,JsonDeserializer<ItemStack> {
- override fun serialize(src: ItemStack, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
- return Gson().toJsonTree(src.serialize())
- }
- override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ItemStack {
- return ItemStack.deserialize(Gson().fromJson<Map<String,Any>>(json))
- }
- private inline fun <reified T> Gson.fromJson(json: JsonElement): T = this.fromJson(json, object: TypeToken<T>() {}.type)
- }
服务端paper1.16.5 git-465
HolographicHat 发表于 2021-8-11 17:28
想问问为什么会出现这种错误https://paste.ubuntu.com/p/GhGcxs4Qbc/
服务端paper1.16.5 git-465 ...
Kotlin好评(
正好我前几周遇到这个问题了
解决方案是不要序列化和反序列化ItemStack,而是直接序列化和反序列化你的数据对象,在数据对象的序列化器中序列化ItemStack字段,即可解决问题
学废了。。
贺兰兰 发表于 2021-8-11 18:35
Kotlin好评(
正好我前几周遇到这个问题了
解决方案是不要序列化和反序列化ItemStack,而是直接序列化和反 ...
我想用这种方法把ItemStack换成字符串储存起来,但是遇到了上面一样的StackOverflow报错。
如果说只要把物品转换成字符串,“在数据对象的序列化器中序列化ItemStack字段”具体应该怎么操作呢?
Sssss... 发表于 2021-8-21 16:05
我想用这种方法把ItemStack换成字符串储存起来,但是遇到了上面一样的StackOverflow报错。
如果说只要把 ...
https://github.com/shaokeyibb/Ra ... GiftSerializer.java
这有个示例你可以看一下
单纯建议,不同意勿杠!没有用过Gson,但是看了你的介绍我感觉这个时候还是fastjson比较方便了
本帖最后由 Bryan33 于 2021-8-29 23:03 编辑
ItemMeta自带实现的序列化中会把非标准nbt以base64编码储存在internal字段中
此外我一般纯储存ItemStack时都是通过Protocollib的com.comphenix.protocol.utility.StreamSerializer中的方法进行储存,不过这样不可读
贺兰兰 发表于 2021-8-8 13:56
不完全对,实际上 ItemMeta 的本质就是 NBT,这种 BukkitAPI 提供的 NBT 实现是可以被序列化的
但自定义 ...
ItemMeta自带实现的序列化中会把非标准nbt以base64编码储存在internal字段中
此外我一般纯储存ItemStack时都是通过Protocollib的com.comphenix.protocol.utility.StreamSerializer中的方法进行储存,不过这样不可读
帖主,yyds,真的太有用了
正好需要一个序列化的工具
本帖最后由 迷离丶不羁 于 2021-9-6 10:12 编辑
BukkitAPI已经自带的有ItemStack序列化与反序列化功能了
public static String saveItemStacks(ItemStack itemStack) throws IOException {
String key = getRandomId();
File itemStackConfigFile = new File(CustomStone.DATA_FOLDER + File.separator + "itemStackData.yml");
YamlConfiguration itemStackConfig = YamlConfiguration.loadConfiguration(itemStackSourceFile);
itemStackConfig.set(key, itemStack);
itemStackConfig.save(itemStackConfigFile);
ItemStack data = itemStackConfig.getItemStack(key);
ItemMeta itemMeta = data.getItemMeta();
return key;
}
存储后的效果 :原版弓
'05865848':
==: org.bukkit.inventory.ItemStack
type: BOW
BukkitAPI已经自带的有ItemStack序列化与反序列化功能了
public static String saveItemStacks(ItemStack itemStack) throws IOException {
String key = getRandomId();
File itemStackConfigFile = new File(CustomStone.DATA_FOLDER + File.separator + "itemStackData.yml");
YamlConfiguration itemStackConfig = YamlConfiguration.loadConfiguration(itemStackSourceFile);
itemStackConfig.set(key, itemStack);
itemStackConfig.save(itemStackConfigFile);
ItemStack data = itemStackConfig.getItemStack(key);
ItemMeta itemMeta = data.getItemMeta();
return key;
}
存储后的效果 :原版弓
'05865848':
==: org.bukkit.inventory.ItemStack
type: BOW
HolographicHat 发表于 2021-8-11 17:28
想问问为什么会出现这种错误https://paste.ubuntu.com/p/GhGcxs4Qbc/
服务端paper1.16.5 git-465 ...
堆栈溢出异常,应该是存在对象相互引用造成死循环,比如A对象里有个B对象,B对象里又有个变量指向A对象
6666666666666