使用 SQL 查询更新 XML

Bas*_*asj 6 python xml sql

假设我们有以下 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>
    """)
Run Code Online (Sandbox Code Playgroud)

怎么可能用类似 SQL 的查询语言更新 XML?

示例(伪代码):

etree.query("""UPDATE a.b SET c.text = 'foo' WHERE d.text = '5678'""")
Run Code Online (Sandbox Code Playgroud)

甚至更复杂的链式查询,例如:

UPDATE a.b SET c.text = (SELECT ... FROM ... WHERE ...) WHERE d.text = '5678'
Run Code Online (Sandbox Code Playgroud)

目标是能够使用类似 SQL 的语法仅用一两行代码来修改复杂的 XML 文档。旁注:作为对比,用人工做这个lxmlfind等作品,但要长得多。

Par*_*ait 7

考虑XSLT,一种旨在转换 XML 文档的专用语言,它共享 SQL 的一些属性:

  • 可以被通用应用层调用的专用语言。
  • 行业语言不限于 Python 等任何语言,可以由Saxon 和 Xalan 等独立处理器运行。
  • 声明式风格和语义,可以从应用层接收参数。

Pythonlxml可以运行 XSLT 1.0 脚本,这些脚本只是特殊的 XML 文件。要回答您的第一个查询,请使用<xsl:choose>.

加载文件

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>""")
Run Code Online (Sandbox Code Playgroud)

运行转换

# 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>
Run Code Online (Sandbox Code Playgroud)

不太清楚你的第二个查询。但是如果需要从不同的 XML 文档(对应于不同的 SQL 表)进行查询,XSLT 维护了该document()函数并且可以在包括参数在内的大多数地方调用。调整下面的 XPath。

<xsl:param name="c_val">
   <xsl:value-of select="document('other.xml')/a/b/c[1]">
</xsl:param>
Run Code Online (Sandbox Code Playgroud)

如果不是c_val从应用层传递,请务必在 Python 中删除该参数,否则您将覆盖上面。