Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of using @field:SerializedName annotation instead of @SerializedName?

Normally, I use the @SerializedName annotation to map the JSON fields. But, in Google Architecture Component sample projects, I see they use @field:SerializedName annotation and I can't find anywhere to read the purpose of using that @field annotation.

I'm new to Kotlin and can someone explain or share the ref links to read? Thank you.

@Entity(
    indices = [
        Index("id"),
        Index("owner_login")],
    primaryKeys = ["name", "owner_login"]
)
data class Repo(
    val id: Int,
    @field:SerializedName("name")
    val name: String,
    @field:SerializedName("full_name")
    val fullName: String,
    @field:SerializedName("description")
    val description: String?,
    @field:SerializedName("owner")
    @field:Embedded(prefix = "owner_")
    val owner: Owner,
    @field:SerializedName("stargazers_count")
    val stars: Int
) {...

like image 339
Toe Lie Avatar asked Jan 27 '20 04:01

Toe Lie


People also ask

Why we use@ SerializedName?

The @SerializedName annotation can be used to serialize a field with a different name instead of an actual field name. We can provide the expected serialized name as an annotation attribute, Gson can make sure to read or write a field with the provided name.


3 Answers

While it isn't incorrect to use @field:SerializedName(...) in this case, it is unnecessary: the same bytecode will be generated with or without the addition of field: because either way only the private backing field will get the annotation. I've never seen an actual project in the wild that prefers @field:SerializedName to @SerializedName and I think the use by the Google samples is unnecessarily confusing for beginners. You should feel free to ignore it.

like image 92
Brian Yencho Avatar answered Sep 28 '22 01:09

Brian Yencho


TL;DR: The field: part is known as a "use-site target", and makes it clear that the annotation is applied to the backing field of the Kotlin property. It is not strictly necessary in this case, though it can make the code more readable.


Explanation

To simplify, let's say you have:

data class Repo(
    @field:SerializedName("name")
    var name: String
)

That code is declaring a data class along with its properties via the primary constructor. Kotlin properties are made up of the property itself, a getter, and—if mutable—a setter. What does all this mean for us? It means there's a lot going on here for just one little property. In this context, the annotation on name could1 apply to any one of the following:

  • The constructor parameter.
  • The Kotlin property itself.
  • The getter.
  • The setter.
  • The setter's parameter.
  • The backing field of the property.

By using field:, which is known as a "use-site target", you make it clear the annotation is applied to the backing field of the property. No more ambiguity1.

However, I should note that not using field: in this case would still result in the annotation being applied to the backing field. As described in the documentation below, if no use-site target is used, then Kotlin looks at the @Target meta-annotation. From there, it applies the annotation to the first valid target from param (constructor parameter), property (Koltin property), and field (backing field), in that order. The @SerializedName2 annotation is not applicable to params. And due to it being a Java annotation, it can't possibly be applicable to propertys. But the annotation is applicable to fields, and so that's where it would end up.


1. There's not any true ambiguity, because the rules are clearly defined (see the documentation below). But that's regarding the compiler. For a human, at first glance the code may be unclear, and that's really what matters. The use-site target may not be strictly necessary, but it makes the code more readable (at least, in my opinion).

2. You've tagged this question with gson, and so that's where I assume the annotation is from.


Documentation

Here's the Kotlin documentation discussing this:

Annotation Use-site Targets

When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. To specify how exactly the annotation should be generated, use the following syntax:

class Example(@field:Ann val foo,    // annotate Java field
              @get:Ann val bar,      // annotate Java getter
              @param:Ann val quux)   // annotate Java constructor parameter

The same syntax can be used to annotate the entire file. To do this, put an annotation with the target file at the top level of a file, before the package directive or before all imports if the file is in the default package:

@file:JvmName("Foo")
package org.jetbrains.demo

If you have multiple annotations with the same target, you can avoid repeating the target by adding brackets after the target and putting all the annotations inside the brackets:

class Example {
     @set:[Inject VisibleForTesting]
     var collaborator: Collaborator
}

The full list of supported use-site targets is:

  • file;
  • property (annotations with this target are not visible to Java);
  • field;
  • get (property getter);
  • set (property setter);
  • receiver (receiver parameter of an extension function or property);
  • param (constructor parameter);
  • setparam (property setter parameter);
  • delegate (the field storing the delegate instance for a delegated property).

To annotate the receiver parameter of an extension function, use the following syntax:

fun @receiver:Fancy String.myExtension() { ... }

If you don't specify a use-site target, the target is chosen according to the @Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:

  • param;
  • property;
  • field.
like image 31
Slaw Avatar answered Sep 28 '22 00:09

Slaw


This might help: from the kotlin docs

When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. To specify how exactly the annotation should be generated, use the following syntax:

class Example(@field:Ann val foo,    // annotate Java field
              @get:Ann val bar,      // annotate Java getter
              @param:Ann val quux)   // annotate Java constructor parameter
like image 37
Mohammed Aquib Azad Avatar answered Sep 28 '22 00:09

Mohammed Aquib Azad