"No transformation found: class io.ktor.utils.io.ByteChannelNative" 使用 Ktor 时出错
"No transformation found: class io.ktor.utils.io.ByteChannelNative" error using Ktor
我正在尝试获取和反序列化一些托管在 github 上的数据。
{
"Meals": [
{
"id": "1598044e-5259-11e9-8647-d663bd870b02",
"name": "Tomato pasta",
"quantity": [{
"quantity": 1 },
{
"quantity": 2
},
{
"quantity": 3
}],
"availableFromDate": "1605802429",
"expiryDate": "1905802429",
"info": "Vegetarian",
"hot": false,
"locationLat": 57.508865,
"locationLong": -6.292,
"distance": null
},
{
"id": "2be2d854-a067-43ec-a488-2e69f0f2a624",
"name": "Pizza",
"quantity": [{
"quantity": 1 },
{
"quantity": 2
},
{
"quantity": 3
}
],
"availableFromDate": "1605802429",
"expiryDate": "1905902429",
"info": "Meat",
"hot": false,
"locationLat": 51.509465,
"locationLong": -0.135392,
"distance": null
}
]
}
如果我在本地启动 json-server 那么它就可以完美运行,所以我知道我的数据 class 不是问题。然而,当我尝试从 github link 做同样的事情时,我得到这个错误:
Error Domain=KotlinException Code=0 "No transformation found: class io.ktor.utils.io.ByteChannelNative -> class kotlin.collections.List
我觉得这可能与设置 ContentType 或类似的东西有关,但到目前为止我还没有成功指定它。
这是我发出请求的代码:
class MealApi {
private val httpClient = HttpClient {
install(JsonFeature) {
val json = Json { ignoreUnknownKeys = true }
serializer = KotlinxSerializer(json)
}
}
suspend fun getAllMeals(): List<Meal> {
return httpClient.get(endpoint)
}
}
这里是我的数据 class 只是为了完整性:
@Serializable
data class Meal(
@SerialName("id")
val id: String,
@SerialName("name")
val name: String,
@SerialName("quantity")
val quantity: List<Quantity>,
@SerialName("availableFromDate")
var availableFromDate: String,
@SerialName("expiryDate")
var expiryDate: String,
@SerialName("info")
val info: String,
@SerialName("hot")
val hot: Boolean,
@SerialName("locationLat")
val locationLat: Float,
@SerialName("locationLong")
val locationLong: Float,
@SerialName("distance")
var distance: Double? = null
)
@Serializable
data class Quantity(
@SerialName("quantity")
val quantity: Int
)
更新
我发现此服务器 https://gitcdn.link/ 允许您使用正确的内容类型提供原始 github 文件。
我搜索了很多如何更改服务器响应 headers(将 plain/text
更改为 application/json
)但似乎 ktor 实际上不允许这样做:
一个不错的方法应该是允许 ResponseObserver
更改服务器响应 header 并通过修改响应。但实际上你不能。
正如您所指出的,您的问题取决于原始 github 页面提供 header Content-Type=plain/text
而不是 ContentType=application/json
.
因此,当您 运行 您在真实服务器中 API 时,IRL 不会发生这种情况,因为您会注意在服务器级别放置正确的内容类型。
但是如果你想解决这个问题,你可以用这种方式重写你的 api 调用:
suspend fun getAllMealsWithFallback() {
var meals: Meals? = null
try {
meals = httpClient.get(endpoint)
} catch (e: NoTransformationFoundException) {
val mealsString: String = httpClient.get(endpoint)
val json = kotlinx.serialization.json.Json {
ignoreUnknownKeys = true
}
meals = json.decodeFromString(mealsString)
} finally {
println("Meals: ${meals?.meals}")
}
}
我不得不添加此 class 以符合您在 github link.
中提供的 json 文本
@Serializable
data class Meals(
@SerialName("Meals")
val meals: List<Meal>,
)
如果问题是内容类型:
您可以更改 KotlinxSerializer
激活的响应内容类型列表,方法是将 JsonFeature
块扩展到:
install(JsonFeature) {
val json = Json { ignoreUnknownKeys = true }
serializer = KotlinxSerializer(json)
receiveContentTypeMatchers += object : ContentTypeMatcher {
override fun contains(contentType: ContentType): Boolean =
contentType == ContentType("text", "plain")
}
}
试试这个:
install(JsonFeature) {
serializer = KotlinxSerializer(KotlinJson { ignoreUnknownKeys = true })
acceptContentTypes = acceptContentTypes + ContentType.Any
}
如果您想接受所有内容类型。或使用 ContentType.Text.Any
、ContentType.Text.Html
(如果您愿意)。
我正在尝试获取和反序列化一些托管在 github 上的数据。
{
"Meals": [
{
"id": "1598044e-5259-11e9-8647-d663bd870b02",
"name": "Tomato pasta",
"quantity": [{
"quantity": 1 },
{
"quantity": 2
},
{
"quantity": 3
}],
"availableFromDate": "1605802429",
"expiryDate": "1905802429",
"info": "Vegetarian",
"hot": false,
"locationLat": 57.508865,
"locationLong": -6.292,
"distance": null
},
{
"id": "2be2d854-a067-43ec-a488-2e69f0f2a624",
"name": "Pizza",
"quantity": [{
"quantity": 1 },
{
"quantity": 2
},
{
"quantity": 3
}
],
"availableFromDate": "1605802429",
"expiryDate": "1905902429",
"info": "Meat",
"hot": false,
"locationLat": 51.509465,
"locationLong": -0.135392,
"distance": null
}
]
}
如果我在本地启动 json-server 那么它就可以完美运行,所以我知道我的数据 class 不是问题。然而,当我尝试从 github link 做同样的事情时,我得到这个错误:
Error Domain=KotlinException Code=0 "No transformation found: class io.ktor.utils.io.ByteChannelNative -> class kotlin.collections.List
我觉得这可能与设置 ContentType 或类似的东西有关,但到目前为止我还没有成功指定它。
这是我发出请求的代码:
class MealApi {
private val httpClient = HttpClient {
install(JsonFeature) {
val json = Json { ignoreUnknownKeys = true }
serializer = KotlinxSerializer(json)
}
}
suspend fun getAllMeals(): List<Meal> {
return httpClient.get(endpoint)
}
}
这里是我的数据 class 只是为了完整性:
@Serializable
data class Meal(
@SerialName("id")
val id: String,
@SerialName("name")
val name: String,
@SerialName("quantity")
val quantity: List<Quantity>,
@SerialName("availableFromDate")
var availableFromDate: String,
@SerialName("expiryDate")
var expiryDate: String,
@SerialName("info")
val info: String,
@SerialName("hot")
val hot: Boolean,
@SerialName("locationLat")
val locationLat: Float,
@SerialName("locationLong")
val locationLong: Float,
@SerialName("distance")
var distance: Double? = null
)
@Serializable
data class Quantity(
@SerialName("quantity")
val quantity: Int
)
更新
我发现此服务器 https://gitcdn.link/ 允许您使用正确的内容类型提供原始 github 文件。
我搜索了很多如何更改服务器响应 headers(将 plain/text
更改为 application/json
)但似乎 ktor 实际上不允许这样做:
一个不错的方法应该是允许 ResponseObserver
更改服务器响应 header 并通过修改响应。但实际上你不能。
正如您所指出的,您的问题取决于原始 github 页面提供 header Content-Type=plain/text
而不是 ContentType=application/json
.
因此,当您 运行 您在真实服务器中 API 时,IRL 不会发生这种情况,因为您会注意在服务器级别放置正确的内容类型。
但是如果你想解决这个问题,你可以用这种方式重写你的 api 调用:
suspend fun getAllMealsWithFallback() {
var meals: Meals? = null
try {
meals = httpClient.get(endpoint)
} catch (e: NoTransformationFoundException) {
val mealsString: String = httpClient.get(endpoint)
val json = kotlinx.serialization.json.Json {
ignoreUnknownKeys = true
}
meals = json.decodeFromString(mealsString)
} finally {
println("Meals: ${meals?.meals}")
}
}
我不得不添加此 class 以符合您在 github link.
中提供的 json 文本@Serializable
data class Meals(
@SerialName("Meals")
val meals: List<Meal>,
)
如果问题是内容类型:
您可以更改 KotlinxSerializer
激活的响应内容类型列表,方法是将 JsonFeature
块扩展到:
install(JsonFeature) {
val json = Json { ignoreUnknownKeys = true }
serializer = KotlinxSerializer(json)
receiveContentTypeMatchers += object : ContentTypeMatcher {
override fun contains(contentType: ContentType): Boolean =
contentType == ContentType("text", "plain")
}
}
试试这个:
install(JsonFeature) {
serializer = KotlinxSerializer(KotlinJson { ignoreUnknownKeys = true })
acceptContentTypes = acceptContentTypes + ContentType.Any
}
如果您想接受所有内容类型。或使用 ContentType.Text.Any
、ContentType.Text.Html
(如果您愿意)。