用xml节点替换文本的Pythonic方法

时间:2011-09-22 08:40:56

标签: python regex lxml

我想知道是否有人能够针对我目前正在努力解决的问题提出更多“pythonic”解决方案。

我有一个源XML文件,我正在编写一个XSLT生成器。源XML的相关部分如下所示:

...
<Notes>
    <Note>
        <Code>ABC123</Code>
        <Text>Note text contents</Text>
        ...
    </Note>
    <Note>
        ...
    </Note>
    ...
</Notes>
...

我有一些与此类似的对象:

from lxml.builder import ElementMaker

#This element maker has the target output namespace
TRGT = ElementMaker(namespace="targetnamespace")
XSL = ElementMaker(namespace="'http://www.w3.org/1999/XSL/Transform',
                   nsmap={'xsl':'http://www.w3.org/1999/XSL/Transform'})

#This is the relevant part of the 'generator output spec'
details = {'xpath': '//Notes/Note', 'node': 'Out', 'text': '{Code} - {Text}'}

目的是从'details'对象生成以下的XSLT片段:

<xsl:for-each select="//Notes/Note">
    <Out><xsl:value-of select="Code"/> - <xsl:value-of select="Text"/></Out>
</xsl:for-each>

我遇到困难的部分是用XML节点替换{placeholder}文本。我最初尝试这样做:

import re
text = re.sub('\{([^}]*)\}', '<xsl:value-of select="\\1"/>', details['text'])
XSL('for-each', 
    TRGT(node, text)
    select=details['xpath'])

但这会逃脱尖括号角色(即使它有效,如果我挑剔它意味着我很好地命名空间的ElementMakers被绕过,我不喜欢):

<xsl:for-each select="//Notes/Note">
    <Out>&lt;xsl:value-of select="Code"/&gt; - &lt;xsl:value-of select="Text"/&gt;</Out>
</xsl:for-each>

目前我有这个,但感觉不是很好:

start = 0
note_nodes = []

for match in re.finditer('\{([^}]*)\}', note):
    text_up_to = note[start:match.start()]
    match_node = self.XSL('value-of', select=note[match.start()+1:match.end()-1])
    start = match.end()

    note_nodes.append(text_up_to)
    note_nodes.append(match_node)

text_after = note[start:]
note_nodes.append(text_after)

XSL('for-each', 
    TRGT(node, *note_nodes)
    select=details['xpath'])

是否有更好的方法(例如将正则表达式拆分为列表,然后将函数应用于匹配的元素)或者我只是过于挑剔?

谢谢!

1 个答案:

答案 0 :(得分:1)

note_nodes=re.split(r'\{(.*?)\}',details['text'])
# ['', 'Code', ' - ', 'Text', '']
note_nodes=[n if i%2==0 else XSL('value-of',select=n) 
            for i,n in enumerate(note_nodes)]