当变量不可为空时,在请求正文中传递 null 时的 NPE

NPE when passing null in request body when variable is not nullable

提问人:Hasherino 提问时间:10/24/2023 最后编辑:Hasherino 更新时间:10/24/2023 访问量:57

问:

我有一个带有 REST API 的 Kotlin 应用程序。终结点接受请求正文,该正文被反序列化为以下数据类:POST

data class RequestBody(
    val ids: Ids? = null,
)

data class Ids(
    val include: List<String>? = null,
    val exclude: List<String>? = null
)

使用以下命令调用此终结点时:

{
   "ids": {
        "include": [
            null
        ]
    },
}

尝试使用 kotlinx JSON 序列化器序列化 RequestBody 时,我得到了一个 NPE:

val jsonSerializer = Json { encodeDefaults = true }

fun getData(
        ids: Ids
    ): Output {
    val inputJson = jsonSerializer.encodeToJsonElement(
        ids = Ids ?: Ids()
    )

    ...
}

错误信息:java.lang.NullPointerException: Parameter specified as non-null is null: method kotlinx.serialization.internal.StringSerializer.serialize

对我来说,问题似乎出在请求正文的反序列化期间,因为,例如,如果我使 not nullable: ,然后尝试使用 调用端点,那么我会得到一个预期的错误: 。 那么,为什么当我在列表中传递 null 时,即使类型被指定为不可为 null,它也不会抛出此错误呢?Idsval ids: Ids"ids": nullJSON parse error: Instantiation of [...] value failed for JSON property ids due to missing (therefore NULL) value for creator parameter ids which is a non-nullable typeincludeString

编辑:在 Kotlin 文档中,它说 Kotlin 中 NPE 的唯一可能原因是:

  • 对 的显式调用。throw NullPointerException()
  • 下面介绍的运算符的用法。!!
  • 初始化方面的数据不一致,例如:
    • 构造函数中未初始化的可用被传递并在某处使用(“泄漏”)。thisthis
    • 超类构造函数调用一个开放成员,该成员在派生类中的实现使用未初始化的状态。

这看起来不适合其中任何一个?

kotlin null nullpointerexception

评论

1赞 Jorn 10/24/2023
传递带有 null 元素的列表和整个列表null 值是有区别的
0赞 Sweeper 10/24/2023
你首先说你得到了一个NPE,然后你问为什么你没有得到NPE?您还显示序列化代码,但询问反序列化。您能否展示一个最小的可重现示例并描述问题,以及您到底想要发生什么。
0赞 Hasherino 10/24/2023
我问为什么我没有得到一个不是NPE。我是说请求正文被反序列化为数据类(问题真正出在哪里),然后在序列化时得到一个 NPEHttpMessageNotReadableException: JSON parse error
1赞 Jorn 10/24/2023
再。您的编辑: 还有另一种方法可以获取 s:使用 Java 反射。例如,如果您使用其他非 Kotlin 解析器来解析 JSON,它很可能会用元素填充您的元素。NullPointerExceptionList<String>?null
0赞 Joffrey 10/24/2023
您使用什么反序列化库来解析请求正文?如果在反序列化后可以看到列表中有一个值,则序列化的整个过程只是噪音,可以简化为导致相同 NPE 的元素访问null

答:

1赞 Jorn 10/24/2023 #1

是的,您正在将值传递到不接受 s 的 List 中。请尝试发布以下内容:nullnull

{
   "ids": {
        "include": null
    },
}

或者,更新列表以接受值:nullval include: List<String?>? = null

评论

0赞 Hasherino 10/24/2023
>“您正在将 null 值传递到不接受 null 的 List 中”。这就是问题所在,它不应该接受 null,但它确实如此,这会导致代码后面出现问题。如果我设置了:,我希望我看到这种行为,无论哪种方式,我都必须进行安全调用,因为 Kotlin 会知道该列表中可能存在 null。现在我已经明确指定列表中的元素不能为空,我应该得到错误的 json 格式错误,正如我在帖子中指定的那样。val include: List<String?>? = null
2赞 Jorn 10/24/2023
@Hasherino我无法重现该问题。 正确地考虑了可空性。在解析您发布到数据类的 JSON 时(没有可为 null 的元素),我得到kotlinx.serializationException in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 9: Expected string literal but 'null' literal was found
-2赞 Hasherino 10/24/2023 #2

正如 Jorn 所说,这很可能是由于使用非 Kotlin 解析器将请求数据反序列化为数据类造成的。用于解决此问题:filterNotNull()

data class RequestBody(
    val ids: Ids? = null,
)

data class Ids(
    var include: List<String>? = null,
    var exclude: List<String>? = null
) {
    init {
        include = include?.filterNotNull()
        exclude = exclude?.filterNotNull()
    }
}

编辑:在这里为这个特定问题找到了更好的解决方案

评论

2赞 Joffrey 10/24/2023
这只是将问题隐藏在地毯下,并不能解决反序列化问题。