1

To start I have the following Moshi json.

@JsonClass(generateAdapter = true)
data class OrderDetails(
    @Json(name = "_id") val id: Int,
    @Json(name = "status") val status: String,
    @Json(name = "tableNo") val tableNo: Int,
    @Json(name = "serverId") val serverId: Int?,
    @Json(name = "items") val orderItems: List<OrderDetailsItem>
)

All these fields are expected to have data except for serverId. This data is fetched from the server where I can allow the user to select order.

onSeletedOrder
   .map { it.orderDetails.serverId } //blows up here apparently.
   .filterNotNull() //have tried this but it doesn't matter.
   .flatMap { findServerBy(it) }
   .map { "${it.firstname} ${it.lastname}" }

When I map to the serverId above I blow up with an NPE. It's interesting that the map (even though it is optional) does an unsafe cast afterwards. I'd expect it to maintain the optional-ness after the map. I'm assuming this is because of the bridging backwards to RxJava. Curious if anyone has a further explanation on why this is.

akarnokd
  • 69,132
  • 14
  • 157
  • 192
Evan Anger
  • 712
  • 1
  • 5
  • 24
  • Null valuess are not allowed in RxJava so map to null is forbidden and crashes with NPE. You could prefilter with `filter { it.orderDetails.serverId != null }` before the `map`. As for why `ma`p doesn't result in type `Optional` in Kotlin, I have no idea. – akarnokd Nov 28 '19 at 17:06
  • What is the type of `onSeletedOrder`: `Observable`? `Single`? Something else? – Alexey Romanov Nov 29 '19 at 07:24
  • The type is a PublishSubject – Evan Anger Nov 29 '19 at 13:47

3 Answers3

1

RxJava does not allow nulls inside the stream. Ideally you would perform this filter before the items enter the stream, but if you can't do that one workaround you could get away with is to use an empty string in place of null.

onSeletedOrder
   .map { it.orderDetails.serverId.orEmpty() }
   .filter { it.isNotEmpty() }
   .flatMap { findServerBy(it) }
   .map { "${it.firstname} ${it.lastname}" }
Damon Baker
  • 887
  • 7
  • 9
  • I'm not happy with the feedback given with the extra defensive-ness need but this works. I suppose I can do the filter even earlier than the map. So filter { it.orderDetails.serverId != null } then map { it.orderDetails.serverId } – Evan Anger Nov 29 '19 at 13:50
0

For "map, but exclude some elements", RxJava has flatMapMaybe (other types than Observable can also have it with corresponding return type):

// helper
fun <T> T?.toMaybe(): Maybe<T> = if (this != null) Maybe.just(this) else Maybe.empty<T>

onSeletedOrder
   .flatMapMaybe { it.orderDetails.serverId.toMaybe() }
   .flatMap { findServerBy(it) }
   .map { "${it.firstname} ${it.lastname}" }
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
0

The best approach is to format your Data Class with optional data.

@JsonClass(generateAdapter = true)
data class OrderDetails(
    @Json(name = "_id") val id: Int? = 0,
    @Json(name = "status") val status: String? = "Not active",
    @Json(name = "tableNo") val tableNo: Int? = 0,
    @Json(name = "serverId") val serverId: Int? = 0,
    @Json(name = "items") val orderItems: List<OrderDetailsItem>? = listOf()
)

You won't receive any NPE

Pulkit
  • 1,020
  • 3
  • 12
  • 26
  • Thats actually not the case when I start putting these particular optional-fields into RxJava. – Evan Anger Nov 29 '19 at 13:46
  • Interesting, I found that moshi doesn't support this, based on this https://github.com/square/moshi/issues/807 Can you experiment a bit and use, Kotlinx Serialization. I have been using it in my project and this use case is coverd. Just wanted to see, if it would work with RxJava map filter – Pulkit Nov 29 '19 at 14:04
  • I'll take a look at it, but fundamentally as I've researched this, there is no null safety in RxJava streams. It would take a rewrite in order to accommodate from what I've gathered of RxJava. This was a good thread which discussed some of the issues/differences (https://github.com/reactor/reactor-core/issues/979) – Evan Anger Nov 29 '19 at 17:21