Nesneleri Serileştirme
Uyarı
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:
String
s, the most commonly used primitive types and their wrappersLists 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)
Not
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.
Bir oyuncunun kaç elmas kazandığını izleyen bir veri yapısı düşünün. Biraz benzeyebilir:
public class DiamondCounter {
private UUID playerUUID;
private int diamonds;
[...]
}
Ayrıca bu alanlara erişmek için bazı yöntemler varsayalım, bu vb her ikisi de güzel bir yapıcı ayarı.
Creating a Custom TypeSerializer
Bu tür bir veri yapısı yazma ve yükleme çok basit, özel bir :javadoc:`TypeSerializer`sağlar. ``TypeSerializer`` arabirimi, verileri bir nesneden bir yapılandırma düğümüne yazmak için iki yöntem ve belirli bir yapılandırma düğümünden bir nesne oluşturmak için arabirim sağlar.
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());
}
}
Daha sonra bu``TypeSerializer`` yapılandırılarak kaydedilir. Bu genellikle, varsayılan TypeSerializerCollection veya yerel olarak, yapılandırmanızı yüklerken ConfigurationOptions içinde belirtilerek yapılabilir.
Not
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.
** Kod örneği: Bir TypeSerializer’ın genel olarak kaydedilmesi **
import org.spongepowered.configurate.objectmapping.serialize.TypeSerializers;
TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(DiamondCounter.class), new DiamondCounterSerializer());
** Kod örneği: Bir TypeSerializer’ın yerel olarak kaydedilmesi **
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);
Uyarı
Eğer kendi eklentiniz tarafından kullanılmayan türler için özel bir TypeSerializer
sağladıysanız, TypeSerializer
’ın üzerine yazılmasından kaynaklanan diğer eklentiler veya Sponge ile uyumlu olmayanlar eklentiler için yerel olarak kaydetmelisiniz.
Tüyo
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.
ObjectMappers kullanımı
Çoğu durumda serileştirme, harita alanlarını yapılandırma düğümlerine çevirir, böyle bir TypeSerializer
yazmak oldukça sıkıcı bir durum ve yapılandırmada kendi başına yapılmasını istediğimiz bir durumdur. Bunu :javadoc: ConfigSerializable ve :javadoc:` Setting` bölümlerinde açıklayalım.
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;
[...]
}
Yukarıdaki örnek şimdi daha fazla kayıt olmadan yapılandırma düğümlerinden seri hale getirilebilir. @Setting
açıklamaları, bir biçim düğümünü açıklama yapılan alanla eşleştirir. İki isteğe bağlı parametre, value
ve comment
kabul eder. Eğer value
parametresi mevcutsa, alanın kaydedileceği düğümün adını tanımlar. Eğer mevcut değilse alanın adı kullanılır. Yukarıdaki örnekte, açıklama, playerUUID
alanının içeriğinin “playerUUID” ile yorumlandığı ‘’player’’ düğümüne kaydedilmesini sağlar. Bununla birlikte, diamonds
alanı, tam açıklamanın sadece bir yorum belirttiği için bu tam ad altında kaydedilecektir. Uygulama, yorumlanan yapılandırma düğümlerini destekliyorsa, bu yorum yapılandırmaya yazılır, yoksa atılır.
Tüyo
@Setting(value ="someNode")
yerine @Setting("someNode")
kısaltmasını da kullanabilirsiniz
@ConfigSerializable
annotation’ı class için Configurate’in ObjectMapper oluşturmasına olanak sağladığından dolayı başka kayıtlara muhtaç kalınmasını engeller. Tek sınırlama Configurate’in belirlenmiş yerleri doldurabilmek için boş bir constructor’a ihtiyaç duymasıdır.
Not
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
Not
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);
}
Özel bir ObjectMapperFactory sağlanıyor
Kısıtlama, farklı bir ObjectMapperFactory kullandığımızda, örneğin GuiceObjectMapperFactory, ortadan kaldırılabilir. Bu durumda boş bir constuctor’a ihtiyaç duyulması yerine guice’un dependency injenction ile oluşturabileceği tüm class’larda çalışır. Bu ayrıca @Inject
ve @Setting
gibi belirlenmiş yerlerin karışımına da olanak sağlar.
Eklentiniz, dependency injection (bkz Bağımlılık Enjeksiyonu) kullanıp bunu ConfigurationOptions``a okutarak``GuiceObjectMapperFactory
kullanabilir.
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));
}
}
Not
Yukarıda ki kod bir örnektir ve özlük için uygun bir istisna işlemesinden yoksundur.