Szeregowanie niestandardowych danych
Without a method serializing and deserializng, your data will not persist across restarts. Sponge has a few different ways to serialize/deserialize data based on the type of data:
DataSerializables implement an interface to perform serialization, and use DataBuilder for deserialization and creation
DataManipulators also implement
DataSerializable
, but instead use a DataManipulatorBuilder for deserialization and creationObjects that do not or cannot implement
DataSerializable
use DataTranslator for both serialization and deserialization
To oznacza, że praktycznie każdy obiekt w Javie może zostać zapisany na dysku, jeśli został zarejestrowany!
Czytanie DataViews
Kiedy tylko czytasz zserializowany obiekt, kuszącą opcją jest samodzielne odczytywanie wszystkich indywidualnych wartości w celu ręcznego tworzenia wszystkich wymaganych obiektów (i ich parametrów) dla twoich danych. Niemniej jednak w zależności od danych zapisanych w bańce jest kilka sposobów, które są znacznie wygodniejsze:
Common java types such as
int
,String
,double
,List
andMap
can be retrieved using built-in methodsgetInt(DataQuery)
,getString(DataQuery)
, etc. Lists of these types can also be retrieved in a similar fashion, for examplegetStringList(DataQuery)
.DataSerializable
objects can be retrieved usinggetSerializable(DataQuery, Class)
orgetSerializableList(DataQuery, Class)
. Along with the path, you must also specify theClass
of the serializable type, such asHome.class
.Objects with a registered
DataTranslator
can be retrieved usinggetObject(DataQuery, Class)
orgetObjectList(DataQuery, Class)
. A full list of classes that are supported by default can be found in DataTranslators.
In all cases you need to specify a path using a DataQuery. If your data has a corresponding Key
this is
as easy as calling key.getQuery()
. Otherwise, the easiest way to do this is with DataQuery.of("name")
.
Wskazówka
DataQueries can be used to reference data multiple nodes down a tree by using, for example,
DataQuery.of("my", "custom", "data")
.
DataBuilders
Aby sprawić, że obiekt będzie nadawał się do serializacji, najpierw upewnij się, że implementuje DataSerializable. Musisz wprowadzić tylko dwie metody:
getContentVersion()
- this defined the current version of your data.toContainer()
- this is what your builder will be given when attempting to deserialize and object. You can store whatever you want in the returnedDataContainer
, so long as it is also serializable using one of the methods above. Just use theset(DataQuery, Object)
method to save your data to the given path.
Wskazówka
It is recommended that you save the version of your data to the container as well using Queries.CONTENT_VERSION
as the query. This will allow for versioning upgrades with DataContentUpdaters.
Code Example: Implementing 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;
String name = "Spongie";
@Override
public DataContainer toContainer() {
return new MemoryDataContainer()
.set(DataQuery.of("Name"), this.name)
.set(Queries.CONTENT_VERSION, getContentVersion());
}
The next part is to implement a DataBuilder. It’s recommended to extend AbstractDataBuilder as
it will try to upgrade your data if the version is less than the current version. There’s only one method you need to
implement - build(DataView)
, or buildContent(DataView)
if you’re using AbstractDataBuilder
.
You’ll want to check that all the queries you want to retrieve are present using DataView.contains(Key...)
. If not
the data is likely incomplete and you should return Optional.empty()
.
Jeśli wszystko sprawia wrażenie, że tam jest, użyj metod getX
, aby utworzyć wartość i zwrócić nowo utworzony obiekt jako Opcjonalny`.
Ostatecznie, musisz zarejestrować, tego budowniczego, aby mógł zostać znaleziony przez wtyczki. Żeby to zrobić, wywołaj `` DataManager # registerDataBuilder (Class, DataBuilder) `` odwołując się do klasy danych i instancji konstruktora.
DataContentUpdaters
What happens if you change the layout of data in a new version release? DataContentUpdaters solve that
problem. If the serialized object is less than the current version, an AbstractDataBuilder
will try and update the
data before passing it to the builder.
Each updater has an input version and an output version. You should take in the old data and change whatever is needed to upgrade it to a newer layout. If it’s impossible to convert due to missing data, it may be possible instead to provide a default value which is interpreted elsewhere - such as by the main builder or the object itself.
Finally, you must ensure that all DataContentUpdater
s are registerered with
DataManager#registerContentUpdater()
referencing the main data class - this will allow them to be discovered by
the builder.
Code Example: Implenting a DataContentUpdater
org.spongepowered.api.data.persistence.DataContentUpdater
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));
}
}
DataManipulatorBuilders
A DataManipualatorBuilder
is very similar to DataBuilder
, however it adds a few methods directly related to
deserializing manipulators:
create()
should return a new manipulator with default valuescreateFrom(DataHolder)
is similar to the build method, but instead the values should be taken from the DataHolder. If there is no data to be taken from the holder, just return the output ofcreate()
. If the data is incompatible with theDataHolder
, you should instead returnOptional.empty()
.
Just like DataBuilder
, you should read and return your manipulator in the relevant build
method.
DataManipulatorBuilder
s can make use of DataContentUpdaters as well, as long as you implement
AbstractDataBuilder
.
Registering a DataManipulatorBuilder
is also similar to DataBuilder
but uses the register()
method. You
must reference both your mutable and immutable classes in the method, in addition to an instance of your builder.
Informacja
You must reference the implementation classes if you have split the API from the implementaton.
DataTranslators
Often the objects you want to serialize are not objects that implement DataSerializable
, such as Vector3d
or
Date
. To allow these objects you implelement a DataTranslator which handles both the serialization
and deserialization of the object.
The implementation of translate
is identical to toContainer()
and build(DataView)
for a
DataSerializable
as shown above, except that an InvalidDataException
is thrown if data is missing in place of
returning an Optional
.
As with other data, ensure that you register the translator with
DataManager#registerTranslator(Class, DataTranslator)
.