How to serialize an ItemStack into a String or JSON?

Hello!

I am trying to serialize an item for storage in a JSON file that I’ve created. However, I’m not sure how to do this (or how to deserialize the it either)

There are a ton of different methods within the item stack and I’m not sure what i’m supposed to use.

Can someone point me in the right direction, so that I can figure this out? :slight_smile:

Thanks!

You can use Configurate to achieve this.

configNode.getNode("myItem").setValue(TypeToken.of(ItemStack.class), myItem);
ItemStack item = configNode.getNode("myItem").getValue(TypeToken.of(ItemStack.class));

See https://docs.spongepowered.org/en/plugin/configuration/index.html for details.

1 Like

Is it possible to do this without the standard configuration API?

Thanks for your quick response!

If you must, you can get the raw JSON with the following:

StringWriter sink = new StringWriter();
GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSink(() -> new BufferedWriter(sink)).build();
ConfigurationNode node = loader.createEmptyNode();
node.getNode("item").setValue(TypeToken.of(ItemStack.class), item);
loader.save(node);
String json = sink.toString();
1 Like

Wonderful, works like a charm!

I tried to reverse engineer it to deserialize the item stack from the string that provides, however i think i may be doing it wrong, am I misunderstanding the process here?

public static Optional<ItemStack> deserializeItemStack(String itemstr) {
	try {
		StringWriter sink = new StringWriter();
		sink.write(itemstr);
		GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSink(() -> new BufferedWriter(sink)).build();
		ConfigurationNode node = loader.createEmptyNode();
		loader.save(node);
		return Optional.of(node.getValue(TypeToken.of(ItemStack.class)));
	} catch (Exception e) {
		return Optional.empty();
	}
}

You need to set your String as the source not the sink for deserialization.

StringReader source = new StringReader(json);
GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSource(() -> new BufferedReader(source)).build();
ConfigurationNode node = loader.load();
ItemStack item = node.getValue(TypeToken.of(ItemStack.class));

BTW: Any particular reason why you aren’t using the built in configuration API? It will make things like this a lot easier.

1 Like

There are a couple different reasons. Primarily my familiarity with the JSON library that i’m using, but in general I prefer to work at a much lower level, as it allows me a lot more flexibility (of course, at the risk of having code break, or having issues such as this) in what I am able to do, and as an added bonus, I feel like it helps me learn a lot more about what it is that I’m doing than if I weren’t tinkering around. (which is another reason, i like to mess around with the code and see what i can break :stuck_out_tongue: )

By the way, it seems as though I’m having some issues with this deserialization. It looks like configurate is complaining that it is unable to build the ItemStack, however it looks like its pointing to an issue with the DataSerializableTypeSerializer.deserialize() method on line 60, however the source code doesn’t even go that high of a line. I think i may need to update to the newer version of Sponge, i just remembered there was a new version out recently…

Fair enough. Yeah there was an issue with deserializing DataSerializables that I literally fixed yesterday, update to the latest snapshot of SpongeForge or SpongeVanilla and you should be fine.

Hmm.

I’ve updated my sponge version (both for my workspace and for the server itself), and its still giving me problems…

[12:22:38 ERROR] [STDERR]: ninja.leaping.configurate.objectmapping.ObjectMappingExceptio
n: Could not deserialize DataSerializable of type: org.spongepowered.api.item.inventory.
ItemStack
[12:22:38 ERROR] [STDERR]: at org.spongepowered.common.config.DataSerializableTypeS
erializer.lambda$deserialize$37(DataSerializableTypeSerializer.java:48)
[12:22:38 ERROR] [STDERR]: at java.util.Optional.orElseThrow(Unknown Source)
[12:22:38 ERROR] [STDERR]: at org.spongepowered.common.config.DataSerializableTypeS
erializer.deserialize(DataSerializableTypeSerializer.java:48)
[12:22:38 ERROR] [STDERR]: at org.spongepowered.common.config.DataSerializableTypeS
erializer.deserialize(DataSerializableTypeSerializer.java:41)
[12:22:38 ERROR] [STDERR]: at ninja.leaping.configurate.SimpleConfigurationNode.get
Value(SimpleConfigurationNode.java:226)
[12:22:38 ERROR] [STDERR]: at ninja.leaping.configurate.ConfigurationNode.getValue(
ConfigurationNode.java:356)

(this is right up to the point where my code calls the getValue() method)

[12:22:48 INFO]: SpongeVanilla
     Minecraft: 1.8.9
     SpongeAPI: 4.1.0-SNAPSHOT-6066a68
     SpongeVanilla: 1.8.9-4.1.0-BETA-268

This is the correct updated version, right?

Edit: Oh wait, you said snapshot. Thats the ‘bleeding’ tagged builds, right?

Yeah I’m unable to reproduce this with SpongeVanilla: 1.8.9-4.1.0-BETA-268, working fine for me. Can you paste your code?

Sure thing, heres the two methods that I’ve created.

public static Optional<JSONObject> serializeItemStack(ItemStack item) {
	try {
		StringWriter sink = new StringWriter();
		GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSink(() -> new BufferedWriter(sink)).build();
		ConfigurationNode node = loader.createEmptyNode();
		node.getNode("item").setValue(TypeToken.of(ItemStack.class), item);
		loader.save(node);
		return Optional.of(new JSONObject(sink.toString()));
	} catch (Exception e) {
		e.printStackTrace();
		return Optional.empty();
	}
}

public static Optional<ItemStack> deserializeItemStack(JSONObject json) {
	try {
		StringReader source = new StringReader(json.toString());
		GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setSource(() -> new BufferedReader(source)).build();
		ConfigurationNode node = loader.load();
		return Optional.of(node.getValue(TypeToken.of(ItemStack.class)));
	} catch (Exception e) {
		e.printStackTrace();
		return Optional.empty();
	}
}

The test that I am performing takes the players held item stack, serializes it (which works perfectly fine), and then to test, immediately passes the JSONObject back into the deserializer. (this is where it fails, and points towards the node.getValue() method)

The toString() method of the JSON library is basically the serialization method, it creates the text version of a JSON object. Its worth noting that keeping it in string form and passing the string back also produced this error.

Remove

node.getNode("item") and just node.setValue(TypeToken.of(ItemStack.class), item) directly. You’re trying to deserialize the ItemStack from a different node than you are serializing it to.

1 Like

Thats it! Silly me :slight_smile:

Thank you so much! I really really appreciate your help today!

1 Like

No problem, happy to help! :slight_smile: