I'm working with TornadoFX and would really like to be able to serialize classes with JavaFX Data properties, for instance like this:
@Serializable
class GlobalApplicationData {
@Transient
val testStringProperty = SimpleStringProperty("")
var testString by projectParentPathProperty // <- a delegate by TornadoFX that will call the propertys get and set methods
}
This does not work, a serialization attempt will result in
java.lang.NoSuchFieldError: testString
What is the best approach to serialize data like that with this serializer? is it feasible nicely, without too much boilerplate?
What are you exactly trying to achieve? It's hard to imagine reasonable semantics for serializing/deserializng delegated properties, so they are usually should be marked with @Transient.
testStringProperty in your case can be serializable with some custom serializer written by you. I don't know the specifics of JavaFX, but probably serializing it like a string should work.
I would like to serialize the string (in this case testString) like a string as if it was just a regular variable.
Javafx properties as i understand them sre basically just wrappers for values of a type, which allows for an implementation to offer hooks for getting/setting.
So in this case it would be easy to de/serialize in a generic way.
Would it be possible to automatically de/serialize any value of type Property
I woulld like to do it like this:
@Serializer(forClass = Property::class)
class PropertySerializer<T> : KSerializer<Property<T>> {
override fun load(input: KInput): Property<T> = SimpleObjectProperty<T>(input.read())
override fun save(output: KOutput, obj: Property<T>) {
output.write(obj.value)
}
}
But due to type erasure this does not work:

Can it even work somehow @sandwwraith ?
Also: sorry, probably I'm not seeing the obvious here, or I just lack the necessary basic knowledge of the mechanics of the serializing techniques used here:
But what is the problem with semantics for delegated properties?
Cant the serializer just do as in the above code example. but applied to delegates:
feed the value obtained from the delegated getter to the regular serialization process and conversly the deserializer feed the regularly deserialized value to the setter
bump @sandwwraith - could you tell me, if it is possible to write a serializer for a generic Property<T> class that just holds T values and should use the default T serializer always - as shown in the code example?
This should be possible somehow - at least it does work for lists, right?
But I could not get it to work looking at all code examples and documentation I could find.
Yes, I think it is possible, you can refer to ListSerializer indeed. Key catch is to somehow obtain serializer for T. You can add to your PropertySerializer constructor which accept KSerializer<T>: class PropertySerializer<T>(val elementSerial: KSerializer<T>) and plugin should generate correct call to it if property type will be indeed Proeprty<T>. Then, you can use this serializer to call input.readSerializableValue and output.writeSerializableValue.
However, there is another problem here: it seems that testString does not have a backing field, and the plugin doesn't know yet how to bypass this (by calling setter). It is a known limitation that will likely be addressed in the future.
Thanks for the reply - I will try that out asap.
Also I can just move @Transient from testStringProperty to testString, then testString not having a backing field should not be a problem.
As soon as I have a working and tested peace of code, I will share a gist here, and close the issue.
Ok, I see another problem @sandwwraith :
If I add a constructor argument
PropertySerializer<T>(val elementSerial: KSerializer<T>) {
...
}
like you suggested, how do I register the Serializer with the serializer context?
I don't want to have to annotate the properties I serialize.
This is an external serializer and from how I understand the documentation, I need such a context with the registered serializers:
val serializerContext = SerialContext().apply {
registerSerializer(SimpleStringProperty::class, StringPropertySerializer()) // a non-generic version
// registerSerializer(Property::class, PropertySerializer( ? ))
}
Indeed, there is a problem here. Because context serializer resolves real serializers at runtime, generics are erased there – we have only KClass<Property<*>> without its type parameters.
You can either annotate every property with @SerializableWith(PropertySerializer::class), so compiler plugin would generate a correct call to its constructor because it knows the actual type at compile time. Or, register serializer with aPolymorphicSerializer` as its argument, so your property would record actual type information in its serialized form.
Thank you - is there any documentation or examples on how the PolymorphicSerializer is used?
Not sure if it's still relevant, but here is the documentation polymorphism.md