我目前的项目采用改造kotlin json序列化。
我已将Json配置如下:
ignoreUnknownKeys =真isLenient =真}
,这允许我的应用程序使用未知字段解析Json,但是我希望报告
我收到的所有未知的Json字段。
我看不出还有什么能做到这一点。
使用Jackson更快的xml库,这是很容易实现的,但是我希望使用尽可能少的第三方库。
有什么办法可以让我得到宽大的Json序列化和捕获所有未知的Json字段?
发布于 2021-07-21 06:33:57
与另一个答案一样,我将在这个答案中发布的代码更像是黑客,而不是我推荐的适当解决方案,但与其他答案不同:
尽管如此,真正的解决方案可能是kotlinx.serialization
库我鼓励您在GitHub上发布一个特性请求中缺少的特性。维护人员是非常响应和开放的建议!
正如您可以从源jccampanero与评论链接中看到的那样,引入此行为的正确抽象级别将在StreamingJsonDecoder
中。因此,与下面的解决方案不同的是实现自定义格式和复制/粘贴所有Json
代码,并在StreamingJsonDecoder.handleUnknown
中添加少量内容。再说一次,我不会推荐的。
在第(3)款适用的情况下,以下内容可能是适当的。
abstract class ReportUnknownFieldsSerializer<T : Any>(
private val tSerializer: KSerializer<T>
) : JsonTransformingSerializer<T>( tSerializer )
{
override fun transformDeserialize( element: JsonElement ): JsonElement
{
if ( element is JsonObject )
{
val expectedNames = tSerializer.descriptor.elementNames
val actualNames = element.keys
val unexpectedNames = actualNames.minus( expectedNames )
println( unexpectedNames )
}
return element
}
}
@Serializable
data class SomeObject( val expected: String )
object SomeObjectSerializer
: ReportUnknownFieldsSerializer<SomeObject>( SomeObject.serializer() )
@Serializable
data class Wrapper(
@Serializable( SomeObjectSerializer::class )
val someObject: SomeObject
)
class ReportUnknownFieldsSerializerTest
{
@Test
fun unknown_fields_are_reported()
{
val json = Json { ignoreUnknownKeys = true; isLenient = true }
val serialized = """{"someObject":{"expected":"normal","unexpected":"oh"}}"""
// Outputs "[unexpected]"
val wrapper: Wrapper = json.decodeFromString( serialized )
}
}
反序列化时比较预期字段名和指定字段名的使用的是。对于要启用此行为的每个可序列化类成员,需要应用基ReportUnknownFieldSerializer
的实例化。
因此,如前所述,这可能不适用于您的实际用例。您可能不希望为每个成员字段类型创建自定义序列化程序,然后必须在任何地方应用这些序列化程序。如果这是您的用例,您可能希望将其作为JSON序列化格式的一个特性请求给kotlinx.serialization
的维护人员。它可能会在Json
类上增加一个配置选项,例如,一个lambda回调,以任何您想要的方式处理未知字段。
发布于 2021-07-21 04:49:10
您可以使用以下解决方法:将您的Json
格式包装成另一种格式,它将尝试严格地解码输入字符串(使用ignoreUnknownKeys = false
),如果它失败,则报告未知的键并返回到原始的Json
格式。
@ExperimentalSerializationApi
class JsonLoggingUnknownFields(private val kotlinx: Json) : StringFormat by kotlinx {
private val kotlinxStrict = Json(from = kotlinx) { ignoreUnknownKeys = false }
override fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, string: String): T {
val json = kotlinx.parseToJsonElement(string)
return try {
kotlinxStrict.decodeFromJsonElement(deserializer, json)
} catch (e: SerializationException) {
val unknownKeysInTopLevelJsonObject = if (json is JsonObject) {
val actualFields = json.keys
val expectedFields = deserializer.descriptor.elementNames
actualFields - expectedFields
} else emptySet()
log.debug(
if (unknownKeysInTopLevelJsonObject.isNotEmpty()) "Encountered unknown keys: $unknownKeysInTopLevelJsonObject\nCurrent input: $string"
else e.message!!.replace("Use 'ignoreUnknownKeys = true' in 'Json {}' builder to ignore unknown keys.\n", "")
)
kotlinx.decodeFromJsonElement(deserializer, json)
}
}
}
注意,这个技巧只报告顶级JSON对象的所有未知JSON字段,对于嵌套JSON对象(或JSON数组),只报告第一个遇到的未知字段(如果顶级JSON对象中没有未知字段)。
用法:
@Serializable
data class Obj(val x: Int = 0)
@Serializable
data class Obj2(val obj: Obj)
@ExperimentalSerializationApi
fun main() {
val kotlinx = Json {
ignoreUnknownKeys = true
isLenient = true
}
val kotlinxWithLogging = JsonLoggingUnknownFields(kotlinx)
val input1 = "{\"unknownKey1\": 1,\"unknownKey2\": 1, \"x\" : 11}"
println(kotlinxWithLogging.decodeFromString<Obj>(input1)) //Encountered unknown keys: [unknownKey1, unknownKey2]
val input2 = "[{\"unknownKey1\": 1,\"unknownKey2\": 1}]"
println(kotlinxWithLogging.decodeFromString<List<Obj>>(input2)) //Encountered unknown key 'unknownKey1'.
val input3 = "{\"obj\": {\"unknownKey1\": 1,\"unknownKey2\": 1}}"
println(kotlinxWithLogging.decodeFromString<Obj2>(input3)) //Encountered unknown key 'unknownKey1'.
}
https://stackoverflow.com/questions/68389876
复制相似问题