Sérialiser des Données Customisées

Sans méthode de sérialisation et de désérialisation, vos données ne persisteront pas à travers les redémarrages. Sponge dispose de quelques moyens pour sérializer/désérialiser des données selon le type de données :

  • Les DataSerializables implémentent une interface pour effectuer la sérialisation, et utilisent le DataBuilder pour la désérialisation et la création

  • Les DataManipulators implémentent également DataSerializable, mais utilisent plutôt un DataManipulatorBuilder pour la désérialisation et la création

  • Les objets qui n’implémentent pas ou ne peuvent pas implémenter DataSerializable utilisent DataTranslator pour la sérialisation et la désérialisation

Cela signifie que partiquement n’importe quel objet en Java peut être sauvegardé sur le disque si il a été enregistré !

Lire les DataViews

Chaque fois que vous lisez un objet sérialisé, il est tentant de lire toutes les valeurs individuelles vous-même afin de créer manuellement tous les objets requis (et leurs paramètres) pour vos données. Toutefois, selon les données enregistrées dans le contenu, il y a quelques moyens qui sont bien plus pratiques :

  • Les types communs de Java tels que les int, String, double, List et Map peuvent être récupérés à l’aide des méthodes intégrées getInt(DataQuery), getString(DataQuery), etc… Les listes de ces types peuvent aussi être récupérées de la même façon, par exemple getStringList(DataQuery).

  • Les objets DataSerializable peuvent être récupérés en utilisant getSerializable(DataQuery, Class) ou getSerializableList(DataQuery, Class). Avec le chemin, vous devez spécifier la Class des types sérialisables, comme Home.class.

  • Les objets avec un DataTranslator enregistré peuvent être récupérés en utilisant getObject(DataQuery, Class) ou getObjectList(DataQuery, Class). Une liste complète des classes qui sont supportées par défaut peut être trouvée dans DataTranslators.

Dans tous les cas vous avez besoin de spécifier un chemin en utilisant un DataQuery. Si vos données ont une Key correspondante c’est aussi simple que d’appeler key.getQuery(). Sinon, la façon la plus simple de le faire est avec DataQuery.of("name").

Astuce

Les DataQueries peuvent être utilisés pour référencer les données de plusieurs nodes dans une arborescence en utilisant, par exemple, DataQuery.of("my", "custom", "data").

Les DataBuilders

Pour rendre un objet sérialisable, assurez-vous d’abord qu’il implémente DataSerializable. Vous devez seulement implémenter deux méthodes :

  • getContentVersion() - Cela définit la version actuelle de vos données.

  • toContainer() - C’est ce qui sera donné à votre builder lorsque vous essayerez de désérialiser un objet. Vous pouvez stocker ce que vous voulez dans le DataContainer retourné, du tant que c’est aussi sérialisable en utilisant une des méthodes ci-dessus. Utilisez simplement la méthode set(DataQuery, Object) pour sauvegarder vos données au chemin donné.

Astuce

Il est recommendé d’enregistrer la version de vos données vers le conteneur ainsi que d’utiliser Queries.CONTENT_VERSION comme requête. Cela permettra de faire de la gestion de versions avec Les DataContentUpdaters.

Exemple de Code : Implémenter toContainer

import org.spongepowered.api.data.DataContainer;
import org.spongepowered.api.data.DataQuery;
import org.spongepowered.api.data.Queries;
import org.spongepowered.api.data.MemoryDataContainer;

private String name = "Spongie";

@Override
public DataContainer toContainer() {
    return DataContainer.createNew()
            .set(DataQuery.of("Name"), this.name)
            .set(Queries.CONTENT_VERSION, getContentVersion());
}

La prochaine partie consiste à implémenter un DataBuilder. Il est recommendé d’hériter AbstractDataBuilder puisqu’il va essayer de mettre à jour vos données si la version est inférieure à la version actuelle. Il y a une seule méthode que vous avez besoin d’implémenter - build(DataView)`, ou ``buildContent(DataView) si vous utilisez AbstractDataBuilder.

Vous voudrez vérifier que toutes les requêtes que vous voulez récupérer sont présentes en utilisant DataView.contains(Key...). Si non, les données sont probablemement incomplètes et vous devez retourner Optional.empty().

Si tout semble être présent, utilisez les méthodes ` getX`` pour construire des valeurs et retourner un nouvel objet créé en Optional.

Les DataContentUpdaters

Que se passe-t-il si vous changer la disposition des données dans une nouvelle version ? Les DataContentUpdaters résolvent ce problème. Si l’objet sérialisé est inférieure à la version actuelle, un AbstractDataBuilder va essayer de mettre à jour les données avant de le passer au builder.

Chaque updater a une version d’entrée et une version de sortie. Vous devez prendre les anciennes données et changer tout ce qui est nécessaire pour mettre à jour vers une nouvelle disposition. Si c’est impossible de convertir en raison de données manquantes, il est possible de fournir à la place une valeur par défaut qui est interprétée ailleurs - comme par le builder principal ou l’objet lui-même.

Finalement, vous devez vous assurez que tous les DataContentUpdaters sont enregistrés avec DataManager#registerContentUpdater() en référançant la classe de données principale - cela leur permettra d’être découverts par le builder.

Exemple de Code : Implémenter un DataContentUpdater

import org.spongepowered.api.data.persistence.DataContentUpdater
import org.spongepowered.api.text.Text

public class NameUpdater implements DataContentUpdater {

    @Override
    public int getInputVersion() {
        return 1;
    }

    @Override
    public int getOutputVersion() {
        return 2;
    }

    @Override
    public DataView update(DataView content) {
        String name = content.getString(DataQuery.of("Name")).get();

        // For example, version 2 uses a text for the name
        return content.set(DataQuery.of("Name"), Text.of(name));
    }
}

Les DataManipulatorBuilders

Un DataManipulatorBuilder est très similaire au DataBuilder, mais il ajoute quelques méthodes directement associées à la désérialisation des manipulators :

  • create() doit retourner un nouveau manipulator avec les valeurs par défaut

  • createFrom(DataHolder) est similaire à la méthode build, mais à la place, les valeurs doivent être prises sur le DataHolder. Si il n’y a aucune donnée à récupérer depuis le holder, retournez juste la sortie de create(). Si les données sont incompatibles avec le DataHolder, vous devez retourner un Optional.empty() à la place.

Comme le DataBuilder, vous devez lire et retourner votre manipulator dans la méthode build appropriée.

Les DataManipulatorBuilders peuvent faire usage des Les DataContentUpdaters aussi, du temps que vous implémentez AbstractDataBuilder.

Enregistrer un DataManipulatorBuilder est également semblable au DataBuilder mais utilise la méthode register(). Vous devez référencer vos deux classes mutables et immuables dans la méthode, en plus d’une instance de votre builder.

Note

Vous devez référencer les classes d’implémentation si vous avez divisé l’API de l’implémentation.

Les DataTranslators

Souvent les objets que vous voulez sérialiser ne sont pas des objets qui implémentent DataSerializable, comme Vector3d ou Date. Pour autoriser ces objets vous devez implémenter un DataTranslator qui gère à la fois la sérialisation et la désérialisation de l’objet.

L’implémentation de translate est identique à toContainer() et build(DataView) pour un DataSerializable comme montré ci-dessus, excepté qu’une InvalidDataException est levée si les données sont manquantes à la place de retourner un Optional.

Comme pour les autres données, assurez-vous d’enregistrer le translator pendant l’événement GameRegistryEvent.Register<DataTranslator<?>>.