[SPIGOT-1325] Deserialization of ItemStack fails. Created: 05/Dec/15 Updated: 21/Oct/20 Resolved: 06/Dec/15 |
|
| Status: | Resolved |
| Project: | Spigot |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | None |
| Type: | Bug | Priority: | Minor |
| Reporter: | Taylor | Assignee: | Unassigned |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | Inventory, bug | ||
| Environment: |
Linux Ubuntu 14.04LTS |
||
| Issue Links: |
|
||||||||
| Description |
|
When calling ItemStack.deserialize, a casting exception occurs which breaks deserialization. Unable to find source-code formatter for language: text. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at java.lang.Thread.run(Thread.java:745) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:53) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftTask.run(CraftTask.java:71) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at com.lumengaming.giftwrap.service.DataService$$Lambda$16/912274127.run(Unknown Source) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at com.lumengaming.giftwrap.service.DataService.lambda$getPackageById$6(DataService.java:37) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at com.lumengaming.giftwrap.repository.MySqlDataRepository.getPackageById(MySqlDataRepository.java:131) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at com.lumengaming.giftwrap.repository.MySqlDataRepository.readPackages(MySqlDataRepository.java:158) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at com.lumengaming.giftwrap.SerializerUtility.fromJson(SerializerUtility.java:88) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at com.lumengaming.giftwrap.SerializerUtility.deserialize(SerializerUtility.java:139) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: at org.bukkit.inventory.ItemStack.deserialize(ItemStack.java:533) 05.12 22:57:34 [Server] INFO [22:57:34 WARN]: java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer 05.12 22:57:12 [22:57:12 INFO]: Pangamma ran command Message of the Day 05.12 22:57:12 [Connect] User [22:57:12 INFO]: Pangamma, IP 127.0.0.1 05.12 22:57:12 [Server] INFO [22:57:12 INFO]: UUID of player Pangamma is f77b713b-7aa0-3854-8208-1b3550c188ca 05.12 22:53:28 [Se The fix is to make sure the amount is being cast to Number, then the .intValue() method is used to get the int value of the amount. if (args.containsKey("amount")) { amount = ((Number) args.get("amount")).intValue(); } |
| Comments |
| Comment by kamcio96 [ 21/Oct/20 ] |
|
@md_5 fixed in code? I can't find related commit |
| Comment by Taylor [ 06/Dec/15 ] |
|
You rock, man. Good job! |
| Comment by md_5 [ 06/Dec/15 ] |
|
Issue was fixed. |
| Comment by Taylor [ 06/Dec/15 ] |
|
@md_5 What alternative would you suggest instead of the current solution then? There might be some already-defined method I'm overlooking right now. |
| Comment by Taylor [ 06/Dec/15 ] |
|
I'm using google gson to parse the json into the map types... Then ItemStack.deserialize from there... Warning: Prototype code that looks terrible and will need to be refactored later coming in: package com.lumengaming.giftwrap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import org.bukkit.Material; import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; public class SerializerUtility { //<editor-fold defaultstate="collapsed" desc="Second Attempt"> public static String toJson(final ItemStack[] is){ String json = ""; try{ List<TreeMap<String, Map<String, Object>>> data = serialize(is); Gson gson = new GsonBuilder().serializeNulls().create(); json = gson.toJson(data); // System.out.println(json); // File f = new File("tester.bin"); // FileOutputStream fos; // fos = new FileOutputStream(f); // fos.write(json.getBytes()); } catch (Exception ex){ Logger.getLogger(SerializerUtility.class.getName()).log(Level.SEVERE, null, ex); } return json; } public static String toJsonSingle(final ItemStack is){ String json = ""; try{ List<TreeMap<String, Map<String, Object>>> data = serialize(new ItemStack[]{is}); Gson gson = new GsonBuilder().serializeNulls().create(); json = gson.toJson(data.get(0)); } catch (Exception ex){ Logger.getLogger(SerializerUtility.class.getName()).log(Level.SEVERE, null, ex); } return json; } public static ItemStack fromJsonSingle(final String json){ ItemStack is = null; try{ Gson gson = new GsonBuilder().serializeNulls().create(); // ArrayList<TreeMap<String, Map<String, Object>>> TypeToken tt = new TypeToken<TreeMap<String, Map<String, Object>>>(){}; Type collectionType = tt.getType(); TreeMap<String, Map<String, Object>> fromJson = gson.fromJson(json, collectionType); ArrayList<TreeMap<String, Map<String, Object>>> list = new ArrayList<>(); list.add(fromJson); ItemStack[] deserialize = deserialize(list); return deserialize[0]; } catch (Exception ex){ Logger.getLogger(SerializerUtility.class.getName()).log(Level.SEVERE, null, ex); } return is; } public static ItemStack[] fromJson(final String json){ ItemStack[] is = null; try{ Gson gson = new GsonBuilder().serializeNulls().create(); // ArrayList<TreeMap<String, Map<String, Object>>> TypeToken tt = new TypeToken<ArrayList<TreeMap<String, Map<String, Object>>>>(){}; Type collectionType = tt.getType(); List<TreeMap<String, Map<String, Object>>> fromJson = gson.fromJson(json, collectionType); return deserialize(fromJson); } catch (Exception ex){ Logger.getLogger(SerializerUtility.class.getName()).log(Level.SEVERE, null, ex); } return is; } private static List<TreeMap<String,Map<String,Object>>> serialize(final ItemStack[] itemStackList) { final List<TreeMap<String,Map<String,Object>>> serialized = new ArrayList<>(); for (ItemStack itemStack : itemStackList) { TreeMap<String,Map<String,Object>> sMap = new TreeMap<>(); ItemMeta im = null; try{ if (itemStack == null){ itemStack = new ItemStack(Material.AIR); } im = itemStack.hasItemMeta() ? itemStack.getItemMeta() : null; Map<String, Object> serializedItemMeta = (im != null) ? im.serialize() : null; sMap.put("ItemMeta",serializedItemMeta); }catch(Exception ex){ ex.printStackTrace(); } itemStack.setItemMeta(null); try{ Map<String, Object> serializedItemStack = itemStack.serialize(); sMap.put("ItemStack",serializedItemStack); }catch(Exception ex){ sMap.put("ItemStack", new ItemStack(Material.BARRIER).serialize()); if (sMap.containsKey("ItemMeta")){ sMap.remove("ItemMeta"); } ex.printStackTrace(); } itemStack.setItemMeta(im); serialized.add(sMap); } return serialized; } private static ItemStack[] deserialize(final List<TreeMap<String,Map<String,Object>>> serialized) { final ItemStack[] itemStackList = new ItemStack[serialized.size()]; int i = 0; for (TreeMap<String,Map<String,Object>> sMap : serialized) { try{ Entry<String, Map<String, Object>> serializedItemStack = sMap.entrySet().iterator().next(); if (sMap.containsKey("ItemStack")){ ItemStack is = ItemStack.deserialize(sMap.get("ItemStack")); if (sMap.containsKey("ItemMeta") && sMap.get("ItemMeta") != null){ ItemMeta im = (ItemMeta) ConfigurationSerialization.deserializeObject(sMap.get("ItemMeta"), ConfigurationSerialization.getClassByAlias("ItemMeta")); is.setItemMeta(im); } itemStackList[i++] = is; }else{ itemStackList[i++] = new ItemStack(Material.AIR); } }catch(Exception ex){ ex.printStackTrace(); itemStackList[i++] = new ItemStack(Material.AIR); } } return itemStackList; } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="Base serializers"> private static List<HashMap<Map<String, Object>, Map<String, Object>>> serializeItemStackList(final ItemStack[] itemStackList) { final List<HashMap<Map<String, Object>, Map<String, Object>>> serializedItemStackList = new ArrayList<HashMap<Map<String, Object>, Map<String, Object>>>(); for (ItemStack itemStack : itemStackList) { HashMap<Map<String, Object>, Map<String, Object>> serializedMap = new HashMap<>(); if (itemStack == null) itemStack = new ItemStack(Material.AIR); ItemMeta im = itemStack.hasItemMeta() ? itemStack.getItemMeta() : null; Map<String, Object> serializedItemMeta = (im != null) ? im.serialize() : null; itemStack.setItemMeta(null); Map<String, Object> serializedItemStack = itemStack.serialize(); itemStack.setItemMeta(im); serializedMap.put(serializedItemStack, serializedItemMeta); serializedItemStackList.add(serializedMap); } return serializedItemStackList; } private static ItemStack[] deserializeItemStackList(final List<HashMap<Map<String, Object>, Map<String, Object>>> serializedItemStackList) { final ItemStack[] itemStackList = new ItemStack[serializedItemStackList.size()]; int i = 0; for (HashMap<Map<String, Object>, Map<String, Object>> serializedItemStackMap : serializedItemStackList) { Entry<Map<String, Object>, Map<String, Object>> serializedItemStack = serializedItemStackMap.entrySet().iterator().next(); ItemStack itemStack = ItemStack.deserialize(serializedItemStack.getKey()); if (serializedItemStack.getValue() != null) { ItemMeta itemMeta = (ItemMeta)ConfigurationSerialization.deserializeObject(serializedItemStack.getValue(), ConfigurationSerialization.getClassByAlias("ItemMeta")); itemStack.setItemMeta(itemMeta); } itemStackList[i++] = itemStack; } return itemStackList; } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="Base64"> /** * Returns empty stack on failure. * @param b64Str * @return */ public static ItemStack[] fromBase64Str(final String b64Str){ if (b64Str == null || "".equals(b64Str.trim())) return new ItemStack[0]; return fromBase64(b64Str.getBytes()); } public static ItemStack[] fromBase64(final byte[] b64){ try{ byte[] oBytes = Base64.getDecoder().decode(b64); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(oBytes)); List<HashMap<Map<String, Object>, Map<String, Object>>> data = (List<HashMap<Map<String, Object>, Map<String, Object>>>) ois.readObject(); return deserializeItemStackList(data); } catch (IOException | ClassNotFoundException ex){ Logger.getLogger(SerializerUtility.class.getName()).log(Level.SEVERE, null, ex); } return new ItemStack[0]; } /** * Returns null on failure. * @param items * @return */ public static String toBase64Str(final ItemStack[] items){ return new String(toBase64(items)); } /** * Returns null on failure. * @param items * @return */ public static byte[] toBase64(final ItemStack[] items){ List<HashMap<Map<String, Object>, Map<String, Object>>> data = serializeItemStackList(items); try{ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(data); oos.flush(); oos.close(); byte[] oBytes = baos.toByteArray(); return Base64.getEncoder().encode(oBytes); } catch (IOException ex){ Logger.getLogger(SerializerUtility.class.getName()).log(Level.SEVERE, null, ex); } return new byte[0]; } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="Json"> // public static toJson(final ItemStack[] is){ // try{ // List<HashMap<Map<String, Object>, Map<String, Object>>> data = serializeItemStackList(is); // // } // catch (IOException | ClassNotFoundException ex){ // Logger.getLogger(SerializerUtility.class.getName()).log(Level.SEVERE, null, ex); // } // return new ItemStack[0]; // } //</editor-fold> } |
| Comment by md_5 [ 06/Dec/15 ] |
|
JSON? The serializer only supports yaml, via the configuration API. |
| Comment by Taylor [ 06/Dec/15 ] |
|
You're right, it does. So I'm not sure why the deserialize method tried to deserialize it as a Double... cast to an Integer. Either way, it's a pretty easy fix. =] [{"ItemMeta":null,"ItemStack":{"type":"DISPENSER","amount":10}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}},{"ItemMeta":null,"ItemStack":{"type":"AIR"}}]
|
| Comment by md_5 [ 06/Dec/15 ] |
|
Show me how you are getting an ItemStack with a Double value as it's amount. |
| Comment by Taylor [ 06/Dec/15 ] |
|
This can cause data loss unless fixed. |
| Comment by md_5 [ 05/Dec/15 ] |
|
Post your serialized file, the serializer always stores amount as an integer. |