Serialización de Datos
Mientras un ImmutableDataManipulator es una buena manera de almacenar datos mientras el servidor está ejecutándose, no persistirá durante el reinicio. Sin embargo, cada implemento DataManipulator de la interfaz :javadoc:`DataSerializable`y así puede ser serializado a un :javadoc:`DataContainer` y deserailizado a un DataBuilder.
Después de esta conversación inicial del DataManipulator
especializado para una estructura más general, el DataContainer
se puede seguir procesando.
DataContainer y DataView
Un DataView es una estructura de propósito general para almacenar cualquier tipo de datos. Soporta múltiples valores e incluso anida DataView
s como un valor, así permite una estructura de árbol. Cada valor es identificado por un DataQuery. Un DataContainer
es una raíz DataView
.
Cada DataSerializable
proporciona un método toContainer()
que creará y devolverá un DataContainer
. Como un ejemplo, llamando toContainer()
en una instancia HealthData producirá un DataContainer
que contiene dos valores, uno para el estado actual y uno para la salud máxima, cada uno identificado por el DataQuery
de la respectiva Llave
.
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
Convertir este contenedor de vuelta a una instancia HealthData
se realiza por el``DataBuilder`` correspondiente. Esos son registrados y administrados por el DataManager. También puede ser obtenido por una instancia válida de :javadoc:`Juego` o utilizando la clase de utilidad Sponge. El DataManager``proporciona un método para obtener el apropiado ``DataBuilder
para deserializar una clase dada y adicionalmente un método abreviado para obtener el DataBuilder
y hacer que realice la deserialización en un solo paso. Los siguientes dos ejemplos de código son funcionalmente equivalentes.
Ejemplo de Código: Deserialización, la manera larga
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();
}
Ejemplo de Código: Deserialización, la manera corta
import org.spongepowered.api.data.manipulator.mutable.entity.HealthData;
public Optional<HealthData> deserializeHealth(DataView container) {
return Sponge.getDataManager().deserialize(HealthData.class, container);
}
La función deserializeHealth
devolverá Optional.empty()` si no hay ``DataBuilder` registrado para ``HealthData
o el DataContainer
suministrado está vacío. Si datos inválidos están presentes en el DataContainer
, una InvalidDataException se producirá.
DataTranslator
En Sponge, generalmente las implementaciones MemoryDataView y MemoryDataContainer son utilizadas, las cuales residen solo en la memoria y así no persistirán después de reiniciar el servidor. Con el fin de almacenar persistentemente un DataContainer
, primero tiene que ser convertido en una representación almacenable.
Utilizando la implementación DataTranslators#CONFIGURATION_NODE para DataTranslator, podemos convertir un DataView
en un ConfigurationNode y viceversa. ConfigurationNode
s puede entonces ser escrito y leído por un archivo persistente utilizando la Biblioteca de Configuración.
Ejemplo de Código: Serializar una instancia HealthData para Configuración
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);
}
Ejemplo de Código: Deserializar una instancia HealthData desde Configuración
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);
}
DataFormat
Una alternativa para utilizar un DataTranslator
es usar DataFormat, que le permite almacenar un DataContainer
en formato HOCON, JSON o NBT. Puede también recrear DataContainers utilizando DataFormats
. Sponge proporciona implementaciones DataFormat
que están disponibles en la clase DataFormats.
Por ejemplo, podemos utilizar el DataFormats#JSON DataFormat
que nos permite crear una representación de JSON de un DataContainer
. La salida JSON puede entonces fácilmente ser almacenada en un base de datos. Podemos luego usar el mismo DataFormat
para recrear el DataContainer
original de este JSON cuando es requerido.
Importaciones para ejemplos de código
import org.spongepowered.api.Sponge;
import org.spongepowered.api.data.DataContainer;
import org.spongepowered.api.data.persistence.DataFormats;
import org.spongepowered.api.item.inventory.ItemStackSnapshot;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
Ejemplo de Código: Serializar un ItemStackSnapshot para formato JSON
String json = DataFormats.JSON.write(itemStack.toContainer());
Ejemplo de Código: Deserializar un ItemStackSnapshot del formato JSON
DataContainer container = DataFormats.JSON.read(json);
Ejemplo de Código: Escribir un ItemStackSnapshot para un archivo utilizando NBT
public void writeItemStackSnapshotToFile(ItemStackSnapshot itemStackSnapshot, Path path) {
DataContainer dataContainer = itemStackSnapshot.toContainer();
try (OutputStream outputStream = Files.newOutputStream(path)) {
DataFormats.NBT.writeTo(outputStream, dataContainer);
} catch (IOException e) {
// For the purposes of this example, we just print the error to the console. However,
// as this exception indicates the file didn't save, you should handle this in a way
// more suitable for your plugin.
e.printStackTrace();
}
}
Ejemplo de Código: Leer un ItemStackSnapshot desde un archivo utilizando NBT
public Optional<ItemStackSnapshot> readItemStackSnapshotFromFile(Path path) {
try (InputStream inputStream = Files.newInputStream(path)) {
DataContainer dataContainer = DataFormats.NBT.readFrom(inputStream);
return Sponge.getDataManager().deserialize(ItemStackSnapshot.class, dataContainer);
} catch (IOException e) {
e.printStackTrace();
}
return Optional.empty();
}