JSON : Placeholder
JSON : Placeholder (https://jsonplaceholder.typicode.com/) 是一個用於測試的 REST API 網站。
以下使用 Kotlin 協程 + Retrofit2 調用該網站的 REST API,獲取字符串以及 JSON 數據。
- GET /posts/1
- GET /posts
- POST /posts
- PUT /posts/1
- DELETE /posts/1
所有 GET API 都返回JSON數據,格式(JSON-Schema)如下:
{
"type":"object",
"properties": {
"userId": {"type" : "integer"},
"id": {"type" : "integer"},
"title": {"type" : "string"},
"body": {"type" : "string"}
}
}
創建工程
打開 Intellij IDEA,File / New / Project...
在 New Project 向導的第1頁,選 Kotlin, Project Template 選 JVM Application,Build System 選 Gradle Kotlin,Project JDK 選 1.8。
在向導的第2頁 Template 選 Console Application
點擊 Finish 按鈕創建工程
build.gradle.kts 內容如下
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.4.32"
application
}
group = "me.xxxx"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(kotlin("test-junit"))
}
tasks.test {
useJUnit()
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "1.8"
}
application {
mainClassName = "MainKt"
}
將 dependencies 這部分的內容改為:
dependencies {
testImplementation(kotlin("test-junit"))
val retrofitVersion = "2.9.0"
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.retrofit2:converter-gson:$retrofitVersion")
implementation("com.squareup.retrofit2:converter-scalars:$retrofitVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt")
}
這一段引用了 Kotlin 協程和 Retrofit 這兩個庫。
其中 Retrofit 這個庫中還包含了 Gson 和字符串的轉換器。
Post 對象
jsonschema2pojo 可以將 JSON 數據或格式自動轉換為 Java 的 POJO 類。
data class Post(val userId: Int, val id: Int, val title: String, val body: String) {
override fun toString() =
"Post {userId = $userId, id = $id, title = \"$title\", body = \"${body.replace("\n", "\\n")}\"}"
}
Post 對象負責 Kotlin 對象與 JSON 數據之間的相互轉換。
由於兩者字段名相同,這里不需要使用注解。
// 如果需要加上注解的話
data class Post(@SerializedName("userId") val userId: Int,
@SerializedName("id") val id: Int,
@SerializedName("title") val title: String,
@SerializedName("body") val body: String) {
// ...
}
Retrofit 接口
interface RestPost {
@GET
suspend fun getPostAsString(@Url url: String): String
@GET("posts/{id}")
suspend fun getPostAsJson(@Path("id") id: Int): Post
@GET("posts")
suspend fun getPosts(): List<Post>
@FormUrlEncoded
@POST("posts")
suspend fun createPost(@Field("userId") userId: Int,
@Field("title") title: String,
@Field("body") body: String): Post
@FormUrlEncoded
@PUT("posts/{id}")
suspend fun updatePost(@Field("userId") userId: Int,
@Path("id") id: Int,
@Field("title") title: String,
@Field("body") body: String): Post
@DELETE("posts/{id}")
suspend fun deletePost(@Path("id") id: Int): String
}
Retrofit 庫使用專用接口調用 REST API。
- 接口中的每一個方法都對應於一種 API 調用。
- 注解 @GET @POST @PUT @DELETE 表示 API 調用時所使用的 HTTP 方法。
- 注解 @GET 中帶的值表示 API 調用時所包含的相對路徑,其中可包含路徑變量。
"posts/{id}" 中的 {id} 為路徑變量。 - 注解 @Url 表示參數為路徑。
- 注解 @Path("id") 表示參數為路徑變量。
- 注解 @Field 表示參數為 HTTP 請求體中的鍵值對。
- 使用注解 @Field 的方法必須加上注解 @FormUrlEncoded。
Retrofit 對象
val retrofitJson: Retrofit = Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitString: Retrofit = Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.addConverterFactory(ScalarsConverterFactory.create())
.build()
- retrofitJson 對象用於處理 REST API 所返回的 JSON 數據。
- retrofitString 對象用於處理 REST API 所返回的字符串數據。
調用 REST API
suspend fun getPostAsString(): String =
retrofitString.create(RestPost::class.java)
.getPostAsString("posts/1")
suspend fun getPostAsJson(): Post =
retrofitJson.create(RestPost::class.java)
.getPostAsJson(1)
suspend fun getPosts(n: Int): List<Post> =
retrofitJson.create(RestPost::class.java)
.getPosts().take(n)
suspend fun createPost(): Post =
retrofitJson.create(RestPost::class.java)
.createPost(101, "test title", "test body")
suspend fun updatePost(): Post =
retrofitJson.create(RestPost::class.java)
.updatePost(101, 1, "test title", "test body")
suspend fun deletePost(): String =
retrofitString.create(RestPost::class.java)
.deletePost(1)
- getPostAsString 函數取出第1個Post,返回字符串
- getPostAsJson 函數取出第1個Post,返回Post對象
- getPosts 函數取出前n個Post,返回n個Post對象
- createPost 函數創建1個Post,返回所創建的Post對象
- updatePost 函數更新第1個Post,返回所更新的Post對象
- deletePost 函數刪除第1個Post,返回字符串
main 函數
fun main(args: Array<String>) = runBlocking {
println(getPostAsString())
println(getPostAsJson())
println(getPosts(2))
println(createPost())
println(updatePost())
println(deletePost())
}
輸出結果
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
Post {userId = 1, id = 1, title = "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", body = "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"}
[Post {userId = 1, id = 1, title = "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", body = "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"}, Post {userId = 1, id = 2, title = "qui est esse", body = "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"}]
Post {userId = 101, id = 101, title = "test title", body = "test body"}
Post {userId = 101, id = 1, title = "test title", body = "test body"}
{}