Closed rodrigohsb closed 6 years ago
JSON.nonstrict
Tks!
Any reason for nonstrict
not be used as default?
Because you easily noticed the problem that it was stricter than desired but the opposite you would most likely not have noticed.
Any reason for nonstrict not be used as default
Yes, by default, we opt-in for maximal safety, including type checks and runtime checks. If frontend sends to backend slightly malformed JSON, it's better to know about it in advance, not when your system failed because field name has a typo and some value was ignored. In most cases, such check prevents errors on integration testing stage.
Though sometimes, you have to ignore missing fields, then you can use nonstrict mode
Could not find any documentation on how to use nonstrict mode if serializer is created with the @Serializable, not manually. Can someone help here?
@fracturizer nonstrict
is the property of Json format, not model. It should work with arbitrary serializer.
In other words, I should use Json.nonstrict.parse() instead of Json.parse(). Found it. Thanks.
Any reason for nonstrict not be used as default
Yes, by default, we opt-in for maximal safety, including type checks and runtime checks. If frontend sends to backend slightly malformed JSON, it's better to know about it in advance, not when your system failed because field name has a typo and some value was ignored. In most cases, such check prevents errors on integration testing stage.
Though sometimes, you have to ignore missing fields, then you can use nonstrict mode
How to use this for retrofit? Does this only apply for custom / manual JSON serialization?
@qwwdfsad @sandwwraith
EDITED
For those doesn't using custom Json
de/serialization, use JsonConfiguration(strictMode = false)
on retrofit converter factory
E.g:
// your retrofit builder
.addConverterFactory(
Json(
JsonConfiguration(strictMode = false)
).asConverterFactory(MediaType.get("application/json"))
)
@mochadwi I think your question is more related to the repository of retrofit converter (https://github.com/JakeWharton/retrofit2-kotlinx-serialization-converter). But yes, I think your solution is correct
Is it possible to save all encountered unknown keys to some Map<String, JsonObj>
value?
e.g.
data class MyData(val x: Int, val unknown: Map<String,JsonObj>)
@ngortheone Currently this is not possible. You can deserialize json to JsonObject
though which is merely a map.
@sandwwraith - is there a recommended way of doing schema migration?
for example, say I add a field in a new version of a data class and send that to a client using the old version. I'd want clients to be able to deserialize, use the fields they know about, and then serialize the object to pass back to the server, without losing the new field if the server sent it. I guess sort of proxy the client data object to the JsonObject and serialize the JsonObject, if that makes sense?
because i guess if i just happily use non strict, on serialisation to send back to the server the new field would be dropped?
@tellisnz-shift Well, if you try not to lose any fields client don't know about, then JsonObject is your way. However, I think it is pretty OK for a client to lose fields it doesn't know about and send back old JSON
Latest versions of both Kotlin Serialization and JakeWharton's Converter should do:
Json {
ignoreUnknownKeys = true
}.asConverterFactory(contentType)
Using the Spring Webclient awaitBody() will trigger Kotlin serialization for the respective @Serializable receiver response class.
Unknown keys exception is thrown on the receiver side since some of the fields are missing.
Is it possible to set ignoreUnknownKeys = true somehow for these receiver classes other than implementing custom serializers?
SenderA(id,name,age) --> GET awaitBody<(id,name)>() throws unknown keys.
val json = Json { ignoreUnknownKeys = true isLenient = true }
val strategies = ExchangeStrategies
.builder()
.codecs { clientDefaultCodecsConfigurer ->
run {
clientDefaultCodecsConfigurer.defaultCodecs()
.kotlinSerializationJsonDecoder(KotlinSerializationJsonDecoder(json))
clientDefaultCodecsConfigurer.defaultCodecs()
.kotlinSerializationJsonEncoder(KotlinSerializationJsonEncoder(json))
}
}.build()
return WebClient
.builder()
.exchangeStrategies(strategies)
.baseUrl(baseUrl!!)
.build()
I set ignoreUnknownKeys on my retrofit configuration
` val contentType = "application/json".toMediaType() val json = Json { ignoreUnknownKeys = true}
val webservice by lazy {
Retrofit.Builder()
.client(
OkHttpClient.Builder()
.connectTimeout(1000, TimeUnit.SECONDS)
.readTimeout(1000, TimeUnit.SECONDS)
.addInterceptor(BenefitsInteceptor(context))
.build())
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(json.asConverterFactory(contentType))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build().create(BenefitsApiService::class.java)
}`
When i get the errorbody on the response i get the same message "Strict JSON encountered unknown key: anotherKey". I display the error to the user with a toast
`class ToastUtils {
companion object{
var json = Json { ignoreUnknownKeys = true }
fun showErrorToastFromJson(context: Context, response:String){
Toast.makeText(context,json.decodeFromString<ErrorResponse>(
JSONObject(response).toString())
.message, Toast.LENGTH_SHORT).show()
}
}
}`
But, i need to indicate the same line of code " var json = Json { ignoreUnknownKeys = true }" which is already set on my retrofit configuration. How can i avoid to declare the same configuration two times?
For future reference, as I turned up here finding a solution to this problem. Just setting the ignoreUnknownKeys property did not suffice. Additionally, I also needed to set explicitNulls to false. Thus, the configuration that worked for me was:
Retrofit.Builder()
...
.addConverterFactory(Json {
ignoreUnknownKeys = true
explicitNulls = false
}.asConverterFactory("application/json".toMediaType()))
I tried some basic tests and I came across an unexpected scenario
This scenario works perfectly:
But when added another key into the json above, like this:
I got this error
Is there any way to escape unmapped keys? Any annotation?