英文:
Kotlin Moshi adapter return null when library throw JsonDataException
问题
We are using old backend. Sometimes list of objects is returned when contract says it should be text (and other ways).
How/where can we handle JsonDataException Expected a string but was BEGIN_OBJECT so we can remove object with null value for wrong parsed data?
{
...
"data":{
"value1":"example",
"value2":2,
"value3":"example",
"value4":"example"
},
"other_data":"example",
"data2":"only string"
}
{
...
"data":"", //how to ignore this part -> set null?
"other_data":"example",
"data2":{ //how to ignore this part?
"value":2
}
}
Should this be handled in adapter, if yes is there a need to parse all json manually?
英文:
We are using old backend. Sometimes list of objects is returned when contract says it should be text (and other ways).
How/where can we handle JsonDataException Expected a string but was BEGIN_OBJECT so we can remove object with null value for wrong parsed data?
{
...
"data":{
"value1":"example",
"value2":2,
"value3":"example",
"value4":"example"
},
"other_data":"example",
"data2":"only string"
}
{
...
"data":"", //how to ignore this part -> set null?
"other_data":"example",
"data2":{ //how to ignore this part?
"value":2
}
}
Should this be handled in adapter, if yes is there a need to parse all json manually?
答案1
得分: 1
是的,你可以为这个目的制作一个 Moshi 适配器。你只需要提前查看,看看它是否是你期望的值,如果不是的话就跳过这个值。
这是一个通用的 Kotlin 适配器示例:
@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
internal annotation class SpecifiedTypeOrNull(val token: JsonReader.Token) {
class Adapter(
private val delegate: JsonAdapter<Any?>,
private val token: JsonReader.Token
) : JsonAdapter<Any?>() {
override fun fromJson(reader: JsonReader): Any? {
if (reader.peek() != token) {
reader.skipValue()
return null
}
return delegate.fromJson(reader)
}
override fun toJson(writer: JsonWriter, value: Any?) {
delegate.toJson(writer, value)
}
class Factory : JsonAdapter.Factory {
override fun create(
type: Type,
annotations: Set<Annotation>,
moshi: Moshi
): JsonAdapter<*>? {
if (annotations.isEmpty()) {
return null
}
var token: JsonReader.Token? = null
var nextAnnotations: MutableSet<Annotation>? = null
for (annotation in annotations) {
if (annotation is SpecifiedTypeOrNull) {
token = annotation.token
nextAnnotations = annotations.toMutableSet()
nextAnnotations -= annotation
}
}
if (token == null) {
return null
}
val delegate = moshi.nextAdapter<Any?>(this, type, nextAnnotations!!)
return Adapter(delegate, token)
}
}
}
}
以下是如何在你的代码中使用它的示例:
@JsonClass(generateAdapter = true)
data class Foo(
@SpecifiedTypeOrNull(JsonReader.Token.BEGIN_OBJECT) val data: Data?,
val other_data: String,
@SpecifiedTypeOrNull(JsonReader.Token.STRING) val data2: String?
) {
@JsonClass(generateAdapter = true)
data class Data(val value1: String)
}
val encoded1 = """
{
"data":{
"value1":"example",
"value2":2,
"value3":"example",
"value4":"example"
},
"other_data":"example",
"data2":"only string"
}
"""
val encoded2 = """
{
"data":"",
"other_data":"example",
"data2":{
"value":2
}
}
"""
fun main() {
val moshi = Moshi.Builder()
.add(SpecifiedTypeOrNull.Adapter.Factory())
.build()
val adapter = moshi.adapter(Foo::class.java)
val decoded1 = adapter.fromJson(encoded1)
val decoded2 = adapter.fromJson(encoded2)
println(decoded1)
println(decoded2)
}
英文:
Yes, you can make a Moshi adapter for this. You'll just need to peek ahead, see if it's the value you expect, and skip the value if it's not.
Here's a generalized adapter in Kotlin.
@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
internal annotation class SpecifiedTypeOrNull(val token: JsonReader.Token) {
class Adapter(
private val delegate: JsonAdapter<Any?>,
private val token: JsonReader.Token
) : JsonAdapter<Any?>() {
override fun fromJson(reader: JsonReader): Any? {
if (reader.peek() != token) {
reader.skipValue()
return null
}
return delegate.fromJson(reader)
}
override fun toJson(writer: JsonWriter, value: Any?) {
delegate.toJson(writer, value)
}
class Factory : JsonAdapter.Factory {
override fun create(
type: Type,
annotations: Set<Annotation>,
moshi: Moshi
): JsonAdapter<*>? {
if (annotations.isEmpty()) {
return null
}
var token: JsonReader.Token? = null
var nextAnnotations: MutableSet<Annotation>? = null
for (annotation in annotations) {
if (annotation is SpecifiedTypeOrNull) {
token = annotation.token
nextAnnotations = annotations.toMutableSet()
nextAnnotations -= annotation
}
}
if (token == null) {
return null
}
val delegate = moshi.nextAdapter<Any?>(this, type, nextAnnotations!!)
return Adapter(delegate, token)
}
}
}
}
Here's how you can use it for your above code.
@JsonClass(generateAdapter = true)
data class Foo(
@SpecifiedTypeOrNull(JsonReader.Token.BEGIN_OBJECT) val data: Data?,
val other_data: String,
@SpecifiedTypeOrNull(JsonReader.Token.STRING) val data2: String?
) {
@JsonClass(generateAdapter = true)
data class Data(val value1: String)
}
val encoded1 = """
{
"data":{
"value1":"example",
"value2":2,
"value3":"example",
"value4":"example"
},
"other_data":"example",
"data2":"only string"
}
"""
val encoded2 = """
{
"data":"",
"other_data":"example",
"data2":{
"value":2
}
}
"""
fun main() {
val moshi = Moshi.Builder()
.add(SpecifiedTypeOrNull.Adapter.Factory())
.build()
val adapter = moshi.adapter(Foo::class.java)
val decoded1 = adapter.fromJson(encoded1)
val decoded2 = adapter.fromJson(encoded2)
println(decoded1)
println(decoded2)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论