Firestore Document annotations are confusing (fail) when used together #6600
Unanswered
SteveBurkert
asked this question in
Q&A
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Before I start a wall of text explaining myself, I try to explain my issue with an example.
Let's say we have a very basic kotlin data class, that we want to be serialized as a document
to the firestore. It could look like this:
Let's say we initialize this with "hey".
We know this class will end up being parsed by the processor / mapper: CustomClassMapper
And in firestore we will see a beautiful entry holding "myFoo" = "hey" under some document id.
But now let's say we want to change the name of the serialized field, but still want to use camelCase in the kotlin code. In firestore world we might want underscores. So we want to use different property names when serialized.
Like this:
Funny thing, this won't work.
It will still be serialized as "myFoo".
The reason this does not work
Kotlin creates a public getter and a private backing field for myFoo.
While parsing the mapper will see a public getter, that has no annotation and store a value inside his list of properties -> "myFoo" or "myfoo".
The mapper then will see the private field and also see the fields
PropertyName
annotation, resolving the name to "my_foo", which IS NOT inside the properties list and hence, the field and also all of it's annotations (PropertyName, ServerTimestamp, DocumentId) are IGNORED.So basically, the @PropertyName annotation without use-site target is ignored IF the value
is different to the fields name. (Which is the purpose of the annotation? Right?)
But this I can understand. If we would not ignore it, we would end up storing 2
properties / fields for actually 1 field and have then 2 entries in our document in the cloud.
First slight request.
Even though I am not entirely sure how to handle this, I still think this could be done differently. Sure we should not store 1 property and 1 field, but we could still honour the annotations of the field and the getter, by mapping between getter/field name and PropertyName.value.
But now we come to the part that I found really confusing:
In order to resolve our issue we might use use-site targets like so:
This will work. The getter is used for serializing and the backing field...is ignored.
Is ignored? Yes - and all of it's annotations, too.
So let's assume I want to store a serverside timestamp inside my foo.
This will not work as expected and there will be no timestamp generated on the server and the value will be
null
.@ServertimeStamp
will work without PropertyName, or with the field having the same name as the value of PropertyName.Now we have an annotation on the getter and one annotation on the field.
Because of refactoring, change, whatever this might happen.
For example, if your code was previously
all is good. But now you changed it to:
And
my_foo
ends up in firestore...but huh? No timestamp and the value isnull
.Conclusion
I think this is a counterintuitive behaviour that must at least be documented somewhere OR
field site annotations should still be honoured, even if the resolved field name is not already
in the list of properties.
There are plenty of stackoverflow questions regarding the
@PropertyName
annotation not workingor weird serialization that all have to do with the same issue.
Also, even though documented, it feels weird that if you use
@PropertyName
within a document class,you can use it for serialization, but you can not use the same class for deserialization.
Beta Was this translation helpful? Give feedback.
All reactions