Step #3: Defining Our Response

Our “Web service” is going to send us JSON that looks like:

[
  {
    "id": "bce0dde0-5eee-0137-c042-38ca3ad2633d",
    "description": "Write a JSON file containing to-do items",
    "completed": true,
    "notes": "Technically, this work was not completed when I wrote this, though it is completed now",
    "created_on": "2019-05-22"
  },
  {
    "id": "f42d74e8-6fd8-4eb1-a4fe-af1c1314573b",
    "description": "Add a third object to this JSON file",
    "completed": false,
    "notes": "",
    "created_on": "2019-05-22"
  }
]
(from items.json)

This resembles our model objects, but it is not quite identical. Moreover, many times the maintainers of the Web service are not the same developers as those who maintain the Android app (let alone the iOS app, the Web app, etc.). The Web service API might change from time to time.

The recommended way of handling this is to treat the Web service data model as being distinct from the app’s data model, with conversions between them as needed. This is similar to how we have our Room entities defined separately from our models, so any changes in Room do not affect our core app logic. As it turns out, we are going to funnel our server responses into the database, so we will be focusing more on converting Web service responses into entities that we can attempt to insert into the database.

With that in mind, right-click over the com.commonsware.todo.repo package in the java/ directory and choose “New” > “Kotlin File/Class” from the context menu. For the name, fill in ToDoServerItem, and choose “Class” for the kind. Press Enter or Return to create the class. Then, replace the class declaration with:

@JsonClass(generateAdapter = true)
data class ToDoServerItem(
  val description: String,
  val id: String,
  val completed: Boolean,
  val notes: String,
  @Json(name = "created_on") val createdOn: Instant
)

The properties of ToDoServerItem match that of the JSON that we will receive from the Web service, with one exception: the JSON has our creation date in a created_on property, and we would like to use lowerCamelCase formatting for our Kotlin property. The @Json annotation applied to the createdOn property tells Moshi that the created_on value in the JSON goes into this createdOn property on our class.

The @JsonClass annotation applied to ToDoServerItem overall indicates that we want Moshi to code-generate the code that can fill in a ToDoServerItem from a matching JSON object.

This will work, with one exception: Moshi knows only about standard Java/Kotlin primitive types and strings. In particular, Moshi knows nothing about Instant and knows nothing about how to take a value like "2019-05-22" and convert it into a Instant. For that, we need to create an adapter class.

So, below ToDoServerItem in the same file, add this code:

private val FORMATTER = DateTimeFormatter.ISO_INSTANT

class MoshiInstantAdapter {
  @ToJson
  fun toJson(date: Instant) = FORMATTER.format(date)

  @FromJson
  fun fromJson(dateString: String): Instant =
    FORMATTER.parse(dateString, Instant::from)
}

A Moshi type adapter is simply a class with two functions:

In this case, we are using DateTimeFormatter to convert a Instant to and from a string representation, specifically using the representation found in the Web service’s JSON file.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.