使用 Gson 序列化和反序列化 org.bukkit.ItemStack
本文最后更新于 1168 天前,其中的信息可能已经有所发展或是发生改变。

使用 Gson 序列化和反序列化 org.bukkit.ItemStack

写这玩意的原因

昨天肝了一整天 这个插件,为了方便起见我希望使用 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 正确的序列化或是反序列化啦!

扫码关注 HikariLan's Blog 微信公众号,及时获取最新博文!


微信公众号图片

评论

  1. GreatMingCtu
    Windows Edge 115.0.1901.203
    1 年前
    2023-8-13 20:55:22

    具体怎么tojson?传的是ItemStack serialize后的的数据还是 直接传 ItemStack对象?

    • 博主
      GreatMingCtu
      Linux Chrome 115.0.0.0
      1 年前
      2023-8-13 22:10:43

      自然是 serialize 后再 toJson

  2. GreatMingCtu
    Windows Edge 112.0.1722.39
    1 年前
    2023-8-14 11:24:13

    实测还是回丢失meta数据,registerTypeAdapter用和没用没啥区别,我是1.20

    • 博主
      GreatMingCtu
      Linux Chrome 115.0.0.0
      1 年前
      2023-8-14 11:46:43

      这只是一篇用于更方便的将 ItemStack 序列化成 JSON 的文章,并没有说能解决丢失 meta 数据的问题啊…
      在早期的 Bukkit 版本中,未包含在 ItemMeta 中的 NBT 数据确实会随着序列化消失,但是现在应该没有这个问题了

  3. ADP
    Windows Edge 128.0.0.0
    1 月前
    2024-8-27 19:42:16

    我有一个可以大部实现NBT保存的类,如下:
    https://mcbbs2.net/thread-1052-1-1.html
    这是我写的一个代码片段,里面可以基本可以兼容1.17.1的所有NBT,
    这篇文章留给后来者
    如若不允许,我会尝试撤回。

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇