Select 来自 xml 使用 xslt 1.0 的不同节点和总量
Select distinct nodes and total amount from xml using xslt 1.0
我有 XML,应该由 XSLT 1.0 转换。 XML 文件夹 "Fields" 定义每个 "Row" 元素的名称顺序。所以,每个 "Row" 文件夹中的 MaterialCode 都排在第一位,StorageMatCode 排在第二位,"Amount" 排在第三位。我需要删除所有 "MaterialCode" 重复项,但将所有 "Amounts" 放在一个。
输入xml:
<Response xmlns="http://www.sample.ru/sample/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<ObjectType>StorageMats</ObjectType>
<Version>1.0.0</Version>
<Fields>
<Field type="decimal">MaterialCode</Field>
<Field type="decimal">StorageMatCode</Field>
<Field type="decimal">Amount</Field>
</Fields>
</Header>
<Body>
<Row>
<FieldValue>475625947</FieldValue>
<FieldValue>456789</FieldValue>
<FieldValue>1000</FieldValue>
</Row>
<Row>
<FieldValue>804685387</FieldValue>
<FieldValue>273456</FieldValue>
<FieldValue>3047</FieldValue>
</Row>
<Row>
<FieldValue>973681347</FieldValue>
<FieldValue>578357</FieldValue>
<FieldValue>2037</FieldValue>
</Row>
<Row>
<FieldValue>804685387</FieldValue>
<FieldValue>273456</FieldValue>
<FieldValue>5000</FieldValue>
</Row>
</Body>
</Response>
我想要这个XML:
<?xml version="1.0" encoding="UTF-8"?>
<BDStorageMats xmlns="http://www.sample.ru/sample/BDStorageMats/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<BDStorageMat>
<MaterialCode>475625947</MaterialCode>
<Amount>1000</Amount>
</BDStorageMat>
<BDStorageMat>
<MaterialCode>804685387</MaterialCode>
<Amount>8047</Amount>
</BDStorageMat>
<BDStorageMat>
<MaterialCode>973681347</MaterialCode>
<Amount>2037</Amount>
</BDStorageMat>
</BDStorageMats>
我创建了这个 XSLT:
<xsl:stylesheet version="1.0" xmlns="http://www.sample.ru/sample/BDStorageMats/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>
<xsl:key match="*[local-name()= 'Response']/*[local-name()= 'Body']/*[local-name()= 'Row']" name="codeDistinct" use="*[local-name()= 'FieldValue'][count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='MaterialCode']/preceding-sibling::*)+1]"/>
<xsl:template match="/">
<BDStorageMats>
<xsl:variable name="amountPosition" select="count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='Amount']/preceding-sibling::*)+1"/>
<xsl:variable name="materialCodePosition" select="count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='MaterialCode']/preceding-sibling::*)+1"/>
<xsl:for-each select="*[local-name()= 'Response']/*[local-name()= 'Body']/*[local-name()= 'Row'][generate-id() = generate-id(key('codeDistinct', *[local-name()= 'FieldValue'][count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='MaterialCode']/preceding-sibling::*)+1]))[1]]">
<xsl:variable name="keyGroup" select="key('codeDistinct', *[local-name()= 'FieldValue'][count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='MaterialCode']/preceding-sibling::*)+1])"/>
<BDStorageMat>
<MaterialCode>
<xsl:value-of select="(*[local-name()= 'FieldValue'])[$materialCodePosition]"/>
</MaterialCode>
<Amount>
<xsl:value-of select="sum($keyGroup/(*[local-name()= 'FieldValue'])[$amountPosition])"/>
</Amount>
</BDStorageMat>
</xsl:for-each>
</BDStorageMats>
</xsl:template>
</xsl:stylesheet>
它在 Altova 中运行良好,但我的系统使用 Apache Xalan Processor for XSLT,它拒绝来自 XSLT 的这一行:
<Amount>
<xsl:value-of select="sum($keyGroup/(*[local-name()= 'FieldValue']) [$amountPosition])"/>
</Amount>
有没有其他方法可以通过 XSLT 1.0 做我想做的事?
如果您使用 Xalan(或其他支持 EXSLT set:distinct()
扩展功能的处理器),您可以:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://www.sample.ru/sample/"
xmlns:set="http://exslt.org/sets"
exclude-result-prefixes="ns1 set">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="row" match="ns1:Row" use="ns1:FieldValue[1]" />
<xsl:template match="/ns1:Response">
<BDStorageMats xmlns="http://www.sample.ru/sample/BDStorageMats/1.0">
<xsl:for-each select="set:distinct(ns1:Body/ns1:Row/ns1:FieldValue[1])">
<BDStorageMat>
<MaterialCode>
<xsl:value-of select="." />
</MaterialCode>
<Amount>
<xsl:value-of select="sum(key('row', .)/ns1:FieldValue[3])" />
</Amount>
</BDStorageMat>
</xsl:for-each>
</BDStorageMats>
</xsl:template>
</xsl:stylesheet>
请注意 select 输入 XML 中节点的前缀的使用正确。
我有 XML,应该由 XSLT 1.0 转换。 XML 文件夹 "Fields" 定义每个 "Row" 元素的名称顺序。所以,每个 "Row" 文件夹中的 MaterialCode 都排在第一位,StorageMatCode 排在第二位,"Amount" 排在第三位。我需要删除所有 "MaterialCode" 重复项,但将所有 "Amounts" 放在一个。 输入xml:
<Response xmlns="http://www.sample.ru/sample/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<ObjectType>StorageMats</ObjectType>
<Version>1.0.0</Version>
<Fields>
<Field type="decimal">MaterialCode</Field>
<Field type="decimal">StorageMatCode</Field>
<Field type="decimal">Amount</Field>
</Fields>
</Header>
<Body>
<Row>
<FieldValue>475625947</FieldValue>
<FieldValue>456789</FieldValue>
<FieldValue>1000</FieldValue>
</Row>
<Row>
<FieldValue>804685387</FieldValue>
<FieldValue>273456</FieldValue>
<FieldValue>3047</FieldValue>
</Row>
<Row>
<FieldValue>973681347</FieldValue>
<FieldValue>578357</FieldValue>
<FieldValue>2037</FieldValue>
</Row>
<Row>
<FieldValue>804685387</FieldValue>
<FieldValue>273456</FieldValue>
<FieldValue>5000</FieldValue>
</Row>
</Body>
</Response>
我想要这个XML:
<?xml version="1.0" encoding="UTF-8"?>
<BDStorageMats xmlns="http://www.sample.ru/sample/BDStorageMats/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<BDStorageMat>
<MaterialCode>475625947</MaterialCode>
<Amount>1000</Amount>
</BDStorageMat>
<BDStorageMat>
<MaterialCode>804685387</MaterialCode>
<Amount>8047</Amount>
</BDStorageMat>
<BDStorageMat>
<MaterialCode>973681347</MaterialCode>
<Amount>2037</Amount>
</BDStorageMat>
</BDStorageMats>
我创建了这个 XSLT:
<xsl:stylesheet version="1.0" xmlns="http://www.sample.ru/sample/BDStorageMats/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0"/>
<xsl:key match="*[local-name()= 'Response']/*[local-name()= 'Body']/*[local-name()= 'Row']" name="codeDistinct" use="*[local-name()= 'FieldValue'][count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='MaterialCode']/preceding-sibling::*)+1]"/>
<xsl:template match="/">
<BDStorageMats>
<xsl:variable name="amountPosition" select="count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='Amount']/preceding-sibling::*)+1"/>
<xsl:variable name="materialCodePosition" select="count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='MaterialCode']/preceding-sibling::*)+1"/>
<xsl:for-each select="*[local-name()= 'Response']/*[local-name()= 'Body']/*[local-name()= 'Row'][generate-id() = generate-id(key('codeDistinct', *[local-name()= 'FieldValue'][count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='MaterialCode']/preceding-sibling::*)+1]))[1]]">
<xsl:variable name="keyGroup" select="key('codeDistinct', *[local-name()= 'FieldValue'][count(*[local-name()= 'Response']/*[local-name()= 'Header']/*[local-name()= 'Fields']/*[local-name()= 'Field'][.='MaterialCode']/preceding-sibling::*)+1])"/>
<BDStorageMat>
<MaterialCode>
<xsl:value-of select="(*[local-name()= 'FieldValue'])[$materialCodePosition]"/>
</MaterialCode>
<Amount>
<xsl:value-of select="sum($keyGroup/(*[local-name()= 'FieldValue'])[$amountPosition])"/>
</Amount>
</BDStorageMat>
</xsl:for-each>
</BDStorageMats>
</xsl:template>
</xsl:stylesheet>
它在 Altova 中运行良好,但我的系统使用 Apache Xalan Processor for XSLT,它拒绝来自 XSLT 的这一行:
<Amount>
<xsl:value-of select="sum($keyGroup/(*[local-name()= 'FieldValue']) [$amountPosition])"/>
</Amount>
有没有其他方法可以通过 XSLT 1.0 做我想做的事?
如果您使用 Xalan(或其他支持 EXSLT set:distinct()
扩展功能的处理器),您可以:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://www.sample.ru/sample/"
xmlns:set="http://exslt.org/sets"
exclude-result-prefixes="ns1 set">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="row" match="ns1:Row" use="ns1:FieldValue[1]" />
<xsl:template match="/ns1:Response">
<BDStorageMats xmlns="http://www.sample.ru/sample/BDStorageMats/1.0">
<xsl:for-each select="set:distinct(ns1:Body/ns1:Row/ns1:FieldValue[1])">
<BDStorageMat>
<MaterialCode>
<xsl:value-of select="." />
</MaterialCode>
<Amount>
<xsl:value-of select="sum(key('row', .)/ns1:FieldValue[3])" />
</Amount>
</BDStorageMat>
</xsl:for-each>
</BDStorageMats>
</xsl:template>
</xsl:stylesheet>
请注意 select 输入 XML 中节点的前缀的使用正确。