假设我们有以下 XML 文件:
from lxml import etree
root = etree.fromstring("""
<a>
<b>
<c>Hello</c>
<d>1234</d>
</b>
<b>
<c>Bonjour</c>
<d>5678</d>
</b>
</a>
""")
怎么可能用类似 SQL 的查询语言更新 XML? 示例(伪代码):
etree.query("""UPDATE a.b SET c.text = 'foo' WHERE d.text = '5678'""")
甚至更复杂的链式查询,例如:UPDATE a.b SET c.text = (SELECT ... FROM ... WHERE ...) WHERE d.text = '5678'
目标是能够使用类似 SQL 的语法仅用一两行代码来修改复杂的 XML 文档。旁注:作为比较,使用 lxml
手动执行此操作, find
等工作,但要长得多。
最佳答案
考虑 XSLT ,一种旨在转换 XML 文档的专用语言,它共享 SQL 的一些属性:
Python的
lxml
可以运行 XSLT 1.0 脚本,这些脚本只是特殊的 XML 文件。要回答您的第一个查询,请使用 <xsl:choose>
的条件 XSLT 副本。 .加载文档
from lxml import etree
root = etree.fromstring("""\
<a>
<b>
<c>Hello</c>
<d>1234</d>
</b>
<b>
<c>Bonjour</c>
<d>5678</d>
</b>
</a>""")
xsl = etree.fromstring("""\
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- INITIALIZE PARAMETERS -->
<xsl:param name="c_val"/>
<xsl:param name="d_val"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- CONDITIONALLY UPDATE <c> -->
<xsl:template match="c">
<xsl:copy>
<xsl:choose>
<xsl:when test="following-sibling::d = $d_val">
<xsl:value-of select="$c_val"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="text()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>""")
运行改造# INITIALIZE TRANSFORMER
transformer = etree.XSLT(xsl)
# TRANSFORM WITH PARAMETERS
result = transformer(
root,
c_val=etree.XSLT.strparam("foo"),
d_val=etree.XSLT.strparam("5678")
)
# OUTPUT RESULT TO CONSOLE
print(result)
# <a>
# <b>
# <c>Hello</c>
# <d>1234</d>
# </b>
# <b>
# <c>foo</c>
# <d>5678</d>
# </b>
#</a>
不太清楚你的第二个查询。但是如果需要从不同的 XML 文档(对应于不同的 SQL 表)进行查询,XSLT 会维护 document()
函数,可以在大多数地方调用,包括参数。调整下面的 XPath。<xsl:param name="c_val">
<xsl:value-of select="document('other.xml')/a/b/c[1]">
</xsl:param>
如果没有通过 c_val
从应用层,一定要在 Python 中删除该参数,否则你会覆盖上面。
关于python - 使用 SQL 查询更新 XML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66185262/