Serializing Benda

Peringatan

These docs were written for SpongeAPI 7 and are likely out of date. If you feel like you can help update them, please submit a PR!

The Configurate library also provides the means to tweak automatic serialization and deserialization of objects. Per default, a set of data types can be (de)serialized:

  • Strings, the most commonly used primitive types and their wrappers

  • Lists and Sets of serializable values (not including specific implementations)

  • Maps where both keys and values are serializable (not including specific implementations)

  • The types UUID, URL, URI and (regex) Pattern

  • Any enum or CatalogType (FIXME)

  • The text type Component (See also here)

Catatan

If you need special constraints or rules for your serialization (such as sorting the elements in a Set), then you should consider using your own TypeSerializer implementations.

But if you want to write your custom data structures to a config file, this will not be enough.

Bayangkan sebuah struktur data yang melacak berapa banyak berlian yang ditambang pemain. Mungkin terlihat sedikit seperti ini:

public class DiamondCounter {
    private UUID playerUUID;
    private int diamonds;

    [...]
}

Juga asumsikan beberapa metode untuk mengakses bidang tersebut, pengaturan konstruktor yang bagus, baik dari keduanya, dll.

Creating a Custom TypeSerializer

Cara penulisan dan pemuatan struktur data yang sangat mudah adalah memberikan custom: javadoc: TypeSerializer. Antarmuka TypeSerializer` menyediakan dua metode, satu untuk menulis data dari sebuah objek ke node konfigurasi dan satu untuk membuat objek dari simpul konfigurasi yang diberikan.

import com.google.common.reflect.TypeToken;
import org.spongepowered.configurate.objectmapping.ObjectMappingException;
import org.spongepowered.configurate.objectmapping.serialize.TypeSerializer;

public class DiamondCounterSerializer implements TypeSerializer<DiamondCounter> {

    @Override
    public DiamondCounter deserialize(TypeToken<?> type, ConfigurationNode value)
      throws ObjectMappingException {
        UUID player = value.getNode("player").getValue(TypeToken.of(UUID.class));
        int diamonds = value.getNode("diamonds").getInt();
        return new DiamondCounter(player, diamonds);
    }

    @Override
    public void serialize(TypeToken<?> type, DiamondCounter obj, ConfigurationNode value)
      throws ObjectMappingException {
        value.getNode("player").setValue(obj.getPlayerUUID());
        value.getNode("diamonds").setValue(obj.getDiamonds());
    }
}

Ini '' TypeSerializer'' harus kemudian terdaftar dengan Configurate. Ini dapat dilakukan baik secara global, dengan mendaftar untuk :javadoc: default 'TypeSerializerCollection' atau secara lokal, dengan menetapkan itu di :javadoc: 'ConfigurationOptions' ketika loading konfigurasi Anda.

Catatan

ConfigurationOptions are immutable. Every time you try to modify the original instance a new instance is created; so you either have to use the (chained) result directly or update your variable accordingly.

** Contoh Kode: Mendaftarkan TypeSerializer secara global **

import org.spongepowered.configurate.objectmapping.serialize.TypeSerializers;

TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(DiamondCounter.class), new DiamondCounterSerializer());

** Contoh Kode: Mendaftarkan TypeSerializer secara lokal **

import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.ConfigurationOptions;
import org.spongepowered.configurate.objectmapping.serialize.TypeSerializerCollection;
import org.spongepowered.configurate.objectmapping.serialize.TypeSerializers;

TypeSerializerCollection serializers = TypeSerializers.getDefaultSerializers().newChild();
serializers.registerType(TypeToken.of(DiamondCounter.class), new DiamondCounterSerializer());
ConfigurationOptions options = ConfigurationOptions.defaults().setSerializers(serializers);
ConfigurationNode rootNode = someConfigurationLoader.load(options);

Peringatan

Jika Anda memberikan tipe `` TypeSerializer 'khusus untuk jenis yang tidak diperkenalkan oleh plugin Anda sendiri, Anda seharusnya hanya mendaftarkannya secara lokal untuk menghindari konflik dengan plugin atau Sponge lainnya, yang disebabkan oleh `` TypeSerializer` yang akan ditimpa.

Tip

If you need the TypeToken.of(DiamondCounter.class) in multiple places, then you should consider creating a constant for it. You can do it in a similar fashion as Sponge does in the TypeTokens class, or just define the constant inside of your data or serializer class.

Menggunakan ObjectMappers

Karena dalam banyak kasus serialisasi (de) bermuara pada bidang pemetaan ke nodus konfigurasi, menulis semacam `` TypeSerializer`` adalah urusan yang agak membosankan dan sesuatu yang kami ingin Konfigurasi melakukannya sendiri. Jadi, mari anotasikan kelas kami dengan: javadoc: ConfigSerializable dan: javadoc:` Setting` anotasi.

import org.spongepowered.configurate.objectmapping.Setting;
import org.spongepowered.configurate.objectmapping.serialize.ConfigSerializable;

@ConfigSerializable
public class DiamondCounter {

    @Setting(value="player", comment="Player UUID")
    private UUID playerUUID;
    @Setting(comment="Number of diamonds mined")
    private int diamonds;

    [...]
}

Contoh di atas sekarang bisa diserialisasikan dan di deserialized dari node konfigurasi tanpa registrasi lebih lanjut. Anotasi `` @ Setting`` memetakan simpul konfigurasi ke bidang yang diberi catatan. Ini menerima dua parameter opsional, `` value`` dan `` comment``. Jika parameter `` value`` ada, maka nama node akan ditentukan dalam field yang akan disimpan. Jika tidak ada, nama field akan digunakan sebagai gantinya. Jadi dalam contoh di atas, anotasi memastikan bahwa isi field `` playerUUID`` disimpan ke node "player", dikomentari dengan "Pemain UUID". Field `` intan` akan disimpan dengan nama persisnya karena anotasinya hanya menentukan komentar. Komentar itu akan ditulis ke konfigurasi jika implementasinya mendukung simpul konfigurasi yang berkomentar, jika tidak maka akan dibuang.

Tip

Anda boleh juga menggunakan singkatan @Setting("someNode") daripada @Setting(value="someNode")

Anotasi `` @ ConfigSerializable`` menghilangkan kebutuhan akan registrasi apapun karena memungkinkan Configurate hanya menghasilkan sebuah: javadoc: ObjectMapper untuk kelas. Satu-satunya batasan adalah Configurate membutuhkan konstruktor kosong untuk memberi instantiate objek baru sebelum mengisi bidang beranotasi.

Catatan

You can also have fields that are not are not annotated with @Setting in your @ConfigSerializable classes. These fields won't be persisted to config files and can be used to store temporary references for your plugin.

Using Default Values in ConfigSerializable Types

It is also possible to use default values inside of @ConfigSerializable types. You just have to use Java's field initializers (or getters) to set some default values. As long as the entry is not present in the config file the value won't be overwritten.

@ConfigSerializable
public class DiamondCounter {

    @Setting(value="player", comment="Player UUID")
    private UUID playerUUID;

    @Setting(comment="Number of diamonds mined")
    private int diamonds = 0;

    @Setting(comment="The time the player found a diamond last.")
    private LocalDateTime diamonds = LocalDateTime.now();

    [...]
}

Example: Loading a ConfigSerializable Config with Default Values

Instead of loading a default config from the plugin jar itself, it is also possible to just ask Configurate to create it if it is missing.

try {
    this.config = this.configManager.load().<Configuration>getValue(Configuration.TYPE, Configuration::generateDefault);
} catch (ObjectMappingException | IOException e) {
    this.logger.error("Failed to load the config - Using a default", e);
    this.config = Configuration.generateErrorDefault();
}

In this case you load the entire configuration into a Configuration object that contains all of your plugins configuration. Using such a class has the following benefits:

  • Type safety is guaranteed

  • No need to update the configuration file shipped in your plugin

  • You don't need to store lots of references for each of your configuration options

  • You can pass this config (or its parts) into methods or reference it from other classes

  • It is easy to write comments for each attribute in a place that also helps you during development

Catatan

In this case Configuration.generateDefault() is called when the config file is missing or empty. If you still want to load the shipped default config asset you can load it inside of that method. Configuration.generateErrorDefault() is called when there is an error reading or parsing the config. It is not necessary to use separate methods for those cases; you can also use the no-arg constructor, or use an entirely custom solution.

Example: Saving a ConfigSerializable Config

Saving a @ConfigSerializable config is also very simple, as shown by the following example:

try {
    this.configManager.save(this.configManager.createEmptyNode().setValue(Configuration.TYPE, this.config));
} catch (IOException | ObjectMappingException e) {
    this.logger.error("Failed to save the config", e);
}

Menyediakan ObjectMapperFactory kustom

Pembatasan itu, bagaimanapun, dapat dicabut jika kita menggunakan yang berbeda: javadoc: ObjectMapperFactory, misalnya a: javadoc:` GuiceObjectMapperFactory`. Alih-alih membutuhkan konstruktor kosong, ini akan bekerja pada kelas mana pun yang bisa dibuat melalui injeksi ketergantungan. Ini juga memungkinkan untuk campuran bidang `` @ Inject`` dan `` @ Setting`` beranotasi.

Plugin Anda hanya bisa mendapatkan `` GuiceObjectMapperFactory`` hanya dengan suntikan ketergantungan (lihat: doc: ../ injection) dan kemudian menyebarkannya ke` ConfigurationOptions`.

import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GamePreInitializationEvent;
import org.spongepowered.api.plugin.Plugin;
import com.google.inject.Inject;
import org.spongepowered.configurate.commented.CommentedConfigurationNode;
import org.spongepowered.configurate.loader.ConfigurationLoader;
import org.spongepowered.configurate.objectmapping.GuiceObjectMapperFactory;

@Plugin(name="IStoleThisFromZml", id="shamelesslystolen", version="0.8.15", description = "Stolen")
public class StolenCodeExample {

    @Inject private GuiceObjectMapperFactory factory;
    @Inject private ConfigurationLoader<CommentedConfigurationNode> loader;

    @Listener
    public void enable(GamePreInitializationEvent event) throws IOException, ObjectMappingException {
        CommentedConfigurationNode node =
          loader.load(ConfigurationOptions.defaults().setObjectMapperFactory(factory));
        DiamondCounter myDiamonds = node.getValue(TypeToken.of(DiamondCounter.class));
    }
}

Catatan

Kode di atas adalah sebuah contoh dan, untuk singkatnya, tidak memiliki penanganan eksepsi yang tepat.