Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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()
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
1.1k views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...