자료 직렬화하기
While an ImmutableDataManipulator is a good way to store data while the server is running, it will not persist over a restart. However, every DataManipulator implements the DataSerializable interface and thus can be serialized to a DataContainer and deserialized by a DataBuilder.
After this initial conversion from the specialized DataManipulator
to a more general structure, the DataContainer
can be further processed.
DataContainer and DataView
A DataView is a general-purpose structure for holding any kind of data. It supports multiple values and even
nested DataView
s as a value, thus allowing for a tree-like structure. Every value is identified by a
DataQuery. A DataContainer
is a root DataView
.
Every DataSerializable
provides a toContainer()
method which will create and return a DataContainer
.
As an example, calling toContainer()
on a HealthData instance will yield a DataContainer
containing
two values, one for the current and one for the maximum health, each identified by the DataQuery
of the respective
Key
.
import org.spongepowered.api.data.DataContainer;
import org.spongepowered.api.data.key.Keys;
DataContainer serializedHealth = healthData.toContainer();
double currentHealth = serializedHealth.getDouble(Keys.HEALTH.getQuery()).get();
currentHealth == healthData.health().get(); // true
Converting this container back into a HealthData
instance is done by the corresponding DataBuilder
. Those are
registered and managed by the DataManager. It can either be obtained from a valid Game instance
or using the Sponge utility class. The DataManager
provides a method to get the appropriate
DataBuilder
to deserialize a given class and additionally a shorthand method to get the DataBuilder
and have it
do the deserialization in one step. Both of the following code examples are functionally equivalent.
Code Example: Deserialization, the long way
import org.spongepowered.api.data.DataView;
import org.spongepowered.api.data.manipulator.mutable.entity.HealthData;
import org.spongepowered.api.util.persistence.DataBuilder;
import java.util.Optional;
public Optional<HealthData> deserializeHealth(DataView container) {
final Optional<DataBuilder<HealthData>> builder = Sponge.getDataManager().getBuilder(HealthData.class);
if (builder.isPresent()) {
return builder.get().build(container);
}
return Optional.empty();
}
Code Example: Deserialization, the short way
import org.spongepowered.api.data.manipulator.mutable.entity.HealthData;
public Optional<HealthData> deserializeHealth(DataView container) {
return Sponge.getDataManager().deserialize(HealthData.class, container);
}
The deserializeHealth
function will return Optional.empty()
if there is no DataBuilder
registered for
HealthData
or the supplied DataContainer
is empty. If invalid data is present in the DataContainer
, an
InvalidDataException will be thrown.
DataTranslator
In Sponge, generally the implementations MemoryDataView and MemoryDataContainer are used, which
reside in memory only and thus will not persist over a server restart. In order to persistently store a
DataContainer
, it first has to be converted into a storable representation.
Using the DataTranslators#CONFIGURATION_NODE implementation of DataTranslator, we can convert a
DataView
to a ConfigurationNode and vice versa. ConfigurationNode
s can then be written to and read
from persistent files using the Configurate Library.
Code Example: Serializing a HealthData instance to Configurate
import ninja.leaping.configurate.ConfigurationNode;
import org.spongepowered.api.data.persistence.DataTranslator;
import org.spongepowered.api.data.persistence.DataTranslators;
public ConfigurationNode translateToConfig(HealthData data) {
final DataTranslator<ConfigurationNode> translator = DataTranslators.CONFIGURATION_NODE;
final DataView container = data.toContainer();
return translator.translate(container);
}
Code Example: Deserializing a HealthData instance from Configurate
import java.util.Optional;
public Optional<HealthData> translateFromConfig(ConfigurationNode node) {
final DataTranslator<ConfigurationNode> translator = DataTranslators.CONFIGURATION_NODE;
final DataView container = translator.translate(node);
return deserializeHealth(container);
}