[英]Better way to convert attributes with XSLT?
There has got to be a better way of doing this! 必须有一个更好的方法来做到这一点! I want to convert all of the units found in an XML document to base units (eg x value='1.0' units='Mbps'/ becomes \\x value='1000000' units='bps'/).
我想将XML文档中找到的所有单位都转换为基本单位(例如x值='1.0'单位='Mbps'/变成\\ x值='1000000'单位='bps'/)。 I also want to preserve the other attributes found in a node.
我还想保留在节点中找到的其他属性。
I know that I can just iterate through the document with lxml and change the attributes but I figured that this was the perfect task for XSLT. 我知道我可以使用lxml遍历文档并更改属性,但是我认为这对于XSLT是完美的任务。
This XML: 此XML:
<generator>
<enable value="0"/>
<errorPattern value="Off" units=""/>
<errorMode value="Off" units=""/>
<errorRate value="1000000"/>
<bitRate value="1.638" units="Mbps" blah="foo">
<subelement value="2" units="Kbps"/>
</bitRate>
</generator>
becomes: 变成:
<generator>
<enable value="0"/>
<errorPattern value="Off" units=""/>
<errorMode value="Off" units=""/>
<errorRate value="1000000"/>
<bitRate value="1638000" units="bps" blah="foo">
<subelement value="2000" units="bps"/>
</bitRate>
</generator>
The unit test below will do this but the xsl:stylesheet is abysmal. 下面的单元测试可以做到这一点,但是xsl:stylesheet很糟糕。 I thought that I was going to be able to set a multiplier based on the units and then use that multiplier in some common code.
我以为我将能够基于单位设置乘数,然后在一些通用代码中使用该乘数。 However, I had to replicate the template for 'Mbps' and 'Kbps'.
但是,我必须复制“ Mbps”和“ Kbps”的模板。
There has to be a better way right? 必须有更好的方法吧? While still using lxml.
同时仍在使用lxml。
import unittest
from lxml import isoschematron
from lxml import etree
from StringIO import StringIO
xml = '''\
<generator>
<enable value="0"/>
<errorPattern value="Off" units=""/>
<errorMode value="Off" units=""/>
<errorRate value="1000000"/>
<bitRate value="1.638" units="Mbps" blah='foo'>
<subelement value='2' units='Kbps'/>
</bitRate>
</generator>'''
transform_units=etree.XML('''\
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<!-- Convert Mbps to base units -->
<xsl:template match="*[@units='Mbps']">
<xsl:param name='multiplier'>1000000</xsl:param>
<xsl:copy>
<xsl:apply-templates select='@*'/>
<xsl:attribute name='value'><xsl:value-of select='@value * $multiplier'/></xsl:attribute>
<xsl:attribute name='units'>bps</xsl:attribute>
<xsl:apply-templates select='node()'/>
</xsl:copy>
</xsl:template>
<!-- Convert Kbps to base units -->
<xsl:template match="*[@units='Kbps']">
<xsl:param name='multiplier'>1000</xsl:param>
<xsl:copy>
<xsl:apply-templates select='@*'/> <!-- copy all attributes -->
<xsl:attribute name='value'><xsl:value-of select='@value * $multiplier'/></xsl:attribute>
<xsl:attribute name='units'>bps</xsl:attribute>
<xsl:apply-templates select='node()'/> <!-- process child nodes -->
</xsl:copy>
</xsl:template>
</xsl:stylesheet>''')
class Test(unittest.TestCase):
def setUp(self):
self.ns = namespaces={'svrl':'http://purl.oclc.org/dsdl/svrl'}
self.transformUnits = etree.XSLT(transform_units)
def tearDown(self):
pass
def test_transformUnits(self):
doc = etree.fromstring(xml)
print etree.tostring(doc)
res = self.transformUnits(doc)
print etree.tostring(res)
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()
Output: 输出:
pydev debugger: starting (pid: 10828)
Finding files... done.
Importing test modules ... done.
<generator>
<enable value="0"/>
<errorPattern value="Off" units=""/>
<errorMode value="Off" units=""/>
<errorRate value="1000000"/>
<bitRate value="1.638" units="Mbps" blah="foo">
<subelement value="2" units="Kbps"/>
</bitRate>
</generator>
<generator>
<enable value="0"/>
<errorPattern value="Off" units=""/>
<errorMode value="Off" units=""/>
<errorRate value="1000000"/>
<bitRate value="1638000" units="bps" blah="foo">
<subelement value="2000" units="bps"/>
</bitRate>
</generator>
----------------------------------------------------------------------
Ran 1 test in 0.010s
OK
UPDATE 更新
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"></xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@units='Kbps'] | *[@units='Mbps']">
<xsl:variable name="multi">
<xsl:choose>
<xsl:when test="@units = 'Mbps'">1000000</xsl:when>
<xsl:when test="@units = 'Kbps'">1000</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:copy>
<xsl:apply-templates select='@*'/> <!-- copy all attributes -->
<xsl:attribute name="units">bps</xsl:attribute>
<xsl:attribute name="value"><xsl:value-of select="@value * number($multi)"/></xsl:attribute>
<xsl:apply-templates select='node()'/> <!-- process child nodes -->
</xsl:copy>
<units><xsl:value-of select='$multi'/></units>
</xsl:template>
</xsl:stylesheet>
you can use this 你可以用这个
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"></xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="*[matches(@units, '[MK]bps')]">
<xsl:variable name="multi">
<xsl:choose>
<xsl:when test="@units eq 'Mbps'">1000000</xsl:when>
<xsl:when test="@units eq 'Kbps'">1000</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:copy>
<xsl:attribute name="value" select="@value * number($multi)"/>
<xsl:attribute name="units" select="'bps'"/>
<xsl:copy-of select="@* except (@value, @units)"></xsl:copy-of>
<xsl:apply-templates select="node()"></xsl:apply-templates>
</xsl:copy>
</xsl:template>
I had to replicate the template for 'Mbps' and 'Kbps'.
我必须复制“ Mbps”和“ Kbps”的模板。
XSLT (esp. XSLT 1.0) is naturally verbose, and there is nothing wrong with having a template for each case. XSLT(特别是XSLT 1.0)很冗长,每种情况都有一个模板没有错。 If you want to eliminate duplicate code, you could use something like:
如果要消除重复的代码,可以使用类似以下内容的代码:
<xsl:template match="*[contains(@units, 'bps')]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:variable name="prefix" select="substring-before(@units, 'bps')" />
<xsl:attribute name="value">
<xsl:choose>
<xsl:when test="$prefix='M'">
<xsl:value-of select="@value * 1000000"/>
</xsl:when>
<xsl:when test="$prefix='K'">
<xsl:value-of select="@value * 1000"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@value"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="units">bps</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.