在一个小型 Kotlin/Native 应用程序中,我使用 cinterop 和
libxml2
来计算一组 XPath 表达式以提取元素文本和属性值。我以为我已经弄清楚了,但几周后,我的一个测试“突然”失败了:libxml 抱怨 XPath 无效。我在测试代码周围添加了一个循环,实际上,如果有足够的迭代,测试总是会失败。而且它总是在同一个 XPath 表达式(一组 4 个)上失败。这是我可以重现错误的最少代码:
@OptIn(ExperimentalForeignApi::class)
fun getElementTextFake(xml: String, xPathToNode: String): Either<XmlError, String> {
val xPathCValues = xPathToNode.encodeToByteArray().toUByteArray().toCValues()
val doc: xmlDocPtr? = xmlReadDoc(cur = xml.trim().encodeToByteArray().toUByteArray().toCValues(), URL = null, encoding = "UTF-8", options = 0)
val xPathCtx: xmlXPathContextPtr? = doc?.let { xmlXPathNewContext(doc = it) }
val xPathObj: xmlXPathObjectPtr? = xPathCtx?.let { xmlXPathEvalExpression(str = xPathCValues, ctxt = it) }
val nodeSet: xmlNodeSetPtr? = xPathObj?.pointed?.nodesetval
val result =
if (nodeSet == null || nodeSet.pointed.nodeNr == 0) ElementNotFound(xPathToNode, xml).left() else "fakeElementText".right()
xmlXPathFreeObject(xPathObj)
xmlXPathFreeContext(xPathCtx)
xmlFreeDoc(doc)
return result
}
libxml2打印的错误是
XPath error : Invalid expression<br>
//*[local-name()='request']/*[local-name()='processing']P
传入的原始XPath表达式为
//*[local-name()='request']/*[local-name()='processing']
注意表达式字符串末尾的
P
,这使其无效。我的其他 XPath 表达式都不包含大写 P。错误几乎总是在几百次迭代内发生。
测试以单线程运行。
什么可能导致 XPath 表达式字符串的数据损坏?
问题似乎出在字节数组上:
val xPathCValues = xPathToNode.encodeToByteArray().toUByteArray().toCValues()
libxml 似乎依赖于
xmlChar
数组以 0 结尾,以便找到输入的结尾。在数组末尾添加一个零元素解决了这个问题:
val xPathCValues = (xPathToNode.encodeToByteArray().toUByteArray() + 0u).toCValues()
如果需要 0 终止,则在字节数组中附加 0 可以解决该问题。但它并不能解释问题看似随机的本质。