I'd like to serialise null for only one property in my JSON body that is going on a PUT. I don't want to serialize null for any other types in the object. Model class is like this
@Parcel
class User @ParcelConstructor constructor(var college: College?,
var firstname: String?,
var lastname: String?,
var email: String?,
var active: Boolean = true,
var updatedAt: String?,
var gender: String?,
var picture: String?,
var id: String?,
@field: [CollegeField] var collegeInput: String?,
@field: [CollegeField] var otherCollege: String?,)
I only want to serialise collegeInput and otherCollege fields if either of them are null. For example
val user = User(firstname = "foo", lastname=null, collegeInput="abcd", otherCollege = null)
Json will look something like this:
{"user":{
"firstname": "foo",
"collegeInput": "abcd",
"otherCollege": null
}}
Where otherCollege is null, lastname is omitted from the object as by default moshi does not serialise nulls which is what I want, but qualifer fields should be serialized with null values
I tried using
class UserAdapter {
@FromJson
@CollegeField
@Throws(Exception::class)
fun fromJson(reader: JsonReader): String? {
return when (reader.peek()) {
JsonReader.Token.NULL ->
reader.nextNull()
JsonReader.Token.STRING -> reader.nextString()
else -> {
reader.skipValue() // or throw
null
}
}
}
@ToJson
@Throws(IOException::class)
fun toJson(@CollegeField b: String?): String? {
return b
}
@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class CollegeField
I added the adapter to moshi but it never gets called
@Provides
@Singleton
fun provideMoshi(): Moshi {
return Moshi.Builder()
.add(UserAdapter())
.build()
}
@Provides
@Singleton
fun provideRetrofit(client: OkHttpClient, moshi: Moshi, apiConfig: ApiConfig): Retrofit {
return Retrofit.Builder()
.baseUrl(apiConfig.baseUrl)
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
}
If the class doesn’t have a no-arguments constructor, Moshi can’t assign the field’s default value, even if it’s specified in the field declaration. Instead, the field’s default is always 0 for numbers, false for booleans, and null for references.
To hook it all up and parse the json to the data class you need to create a Moshi object, create the adapter instance and then pass the JSON to the adapter: If the JSON response changes and sets a null field in the JSON then the adapter will fail respecting the non null reference of a val property in the data class and throw a clear exception.
} Because JSON field names are always defined with their Java or Kotlin fields, Moshi makes it easy to find fields when navigating between Java or Koltin and JSON. Use @JsonQualifier to customize how a type is encoded for some fields without changing its encoding everywhere.
We can use the @Json annotation to give a new name to any field in any bean that we control: Once we've done this, Moshi immediately understands that this field has a different name in the JSON: 6.2. Transient Fields In certain cases, we may have fields that should not be included in the JSON.
Your toJson adapter method will return null when the qualified string value is null, and the JsonWriter will not write the null value.
Here is a qualifier and adapter factory to install that will work.
@Retention(RUNTIME)
@JsonQualifier
public @interface SerializeNulls {
JsonAdapter.Factory JSON_ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Nullable @Override
public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, SerializeNulls.class);
if (nextAnnotations == null) {
return null;
}
return moshi.nextAdapter(this, type, nextAnnotations).serializeNulls();
}
};
}
Now, the following will pass.
class User(
var firstname: String?,
var lastname: String?,
@SerializeNulls var collegeInput: String?,
@SerializeNulls var otherCollege: String?
)
@Test fun serializeNullsQualifier() {
val moshi = Moshi.Builder()
.add(SerializeNulls.JSON_ADAPTER_FACTORY)
.add(KotlinJsonAdapterFactory())
.build()
val userAdapter = moshi.adapter(User::class.java)
val user = User(
firstname = "foo",
lastname = null,
collegeInput = "abcd",
otherCollege = null
)
assertThat(
userAdapter.toJson(user)
).isEqualTo(
"""{"firstname":"foo","collegeInput":"abcd","otherCollege":null}"""
)
}
Note that you should use the Kotlin support in Moshi to avoid the @field:
oddities.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With