英文:
XSLT Version 1 .net compiler limited options
问题
a. Here is a modified version of V1 without using the key function:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" omit-xml-declaration="yes" method="xml"/>
<xsl:template match="/">
<PremiumSplit>
<xsl:apply-templates select="//VehicleCategory"/>
</PremiumSplit>
</xsl:template>
<xsl:template match="VehicleCategory">
<xsl:variable name="category" select="."/>
<xsl:if test="not(preceding::VehicleCategory[. = current()])">
<xsl:if test="//Cargo/Exposures/Vehicle/CoverageProvided">
<xsl:apply-templates select="//Cargo/Exposures/Vehicle/CoverageProvided">
<xsl:with-param name="category" select="$category"/>
</xsl:apply-templates>
</xsl:if>
<xsl:if test="//AutoLiability/Exposures/Vehicle/CoverageProvided">
<xsl:apply-templates select="//AutoLiability/Exposures/Vehicle/CoverageProvided">
<xsl:with-param name="category" select="$category"/>
</xsl:apply-templates>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template match="Cargo/Exposures/Vehicle/CoverageProvided">
<xsl:param name="category"/>
<xsl:variable name="coverageCode" select="CoverageCode"/>
<xsl:variable name="categoryPremium" select="sum(//Cargo/Exposures/Vehicle[VehicleSequenceNo = current()/../../VehicleSequenceNo]/CoverageProvided[CoverageCode = $coverageCode]/Premium)"/>
<xsl:variable name="totalPremium" select="sum(//Cargo/Exposures/Vehicle/CoverageProvided[CoverageCode = $coverageCode]/Premium)"/>
<xsl:if test="(generate-id() = generate-id(//Cargo/Exposures/Vehicle/CoverageProvided[CoverageCode = $coverageCode][1])) and not(preceding::Vehicle[CoverageSection = 'Cargo' and CoverageCode = $coverageCode and VehicleCategory = $category]) and $categoryPremium != 0">
<Vehicle>
<CoverageSection>Cargo</CoverageSection>
<xsl:copy-of select="$coverageCode"/>
<CoveragePercent>
<xsl:value-of select="$totalPremium div $categoryPremium"/>
</CoveragePercent>
<xsl:copy-of select="$category"/>
</Vehicle>
</xsl:if>
</xsl:template>
<xsl:template match="AutoLiability/Exposures/Vehicle/CoverageProvided">
<xsl:param name="category"/>
<xsl:variable name="coverageCode" select="CoverageCode"/>
<xsl:variable name="categoryPremium" select="sum(//AutoLiability/Exposures/Vehicle[VehicleSequenceNo = current()/../../VehicleSequenceNo]/CoverageProvided[CoverageCode = $coverageCode]/Premium)"/>
<xsl:variable name="totalPremium" select="sum(//AutoLiability/Exposures/Vehicle/CoverageProvided[CoverageCode = $coverageCode]/Premium)"/>
<xsl:if test="(generate-id() = generate-id(//AutoLiability/Exposures/Vehicle/CoverageProvided[CoverageCode = $coverageCode][1])) and not(preceding::Vehicle[CoverageSection = 'AutoLiability' and CoverageCode = $coverageCode and VehicleCategory = $category]) and $categoryPremium != 0">
<Vehicle>
<CoverageSection>AutoLiability</CoverageSection>
<xsl:copy-of select="$coverageCode"/>
<CoveragePercent>
<xsl:value-of select="$totalPremium div $categoryPremium"/>
</CoveragePercent>
<xsl:copy-of select="$category"/>
</Vehicle>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
b. To fix V2, you need to modify the matching conditions to group coverage codes by category correctly. Here's a revised version of V2:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" omit-xml-declaration="yes" method="xml"/>
<xsl:template match="/">
<PremiumSplit>
<xsl:apply-templates select="//VehicleCategory"/>
</PremiumSplit>
</xsl:template>
<xsl:template match="VehicleCategory">
<xsl:variable name="category" select="."/>
<xsl:if test="not(preceding::VehicleCategory[. = current()])">
<AutoLiability>
<xsl:apply-templates select="//AutoLiability/Exposures/Vehicle/CoverageProvided">
<xsl:with-param name="category" select="$category"/>
</xsl:apply-templates>
</AutoLiability>
<Cargo>
<xsl:apply-templates select="//Cargo/Exposures/Vehicle/CoverageProvided">
<xsl:with-param name="category" select="$category"/>
</xsl:apply-templates>
</Cargo>
</xsl:if>
</xsl:template>
<xsl:template match="Cargo/Exposures/Vehicle/CoverageProvided">
<xsl:param name="category"/>
<xsl:variable name="coverageCode" select="CoverageCode"/>
<xsl:variable name="totalPremium" select="sum(//Cargo/Exposures/Vehicle/CoverageProvided[CoverageCode = $coverageCode]/Premium)"/>
<xsl:variable name="categoryPremium" select="sum(//Cargo/Exposures/Vehicle[../VehicleSequenceNo = current()/../VehicleSequenceNo]/CoverageProvided[CoverageCode = $coverageCode]/Premium)"/>
<xsl:if test="(generate-id() = generate-id(//Cargo/Exposures/Vehicle/CoverageProvided[CoverageCode = $coverageCode][1])) and $categoryPremium != 0">
<Vehicle>
<CoverageSection>Cargo</CoverageSection>
<xsl:copy-of select="$coverageCode"/>
<CoveragePercent>
<xsl:value-of select="$totalPremium div $categoryPremium"/>
</CoveragePercent>
<xsl:copy-of select="$category"/>
</Vehicle>
</xsl:if>
</xsl:template>
<xsl:template match="AutoLiability/Exposures/Vehicle/CoverageProvided">
<xsl:param name="category"/>
<xsl:variable name="coverageCode" select="CoverageCode"/>
<xsl:variable
<details>
<summary>英文:</summary>
write xslt code in xslt version 1 without using key function for given input xml (reason - .net compiler cannot accept key function or a variable under use attribute of key function)
<Root>
<ExternalFulfillmentRequest>
<Vehicles>
<Vehicle>
<VehicleSequenceNo>1</VehicleSequenceNo>
<VehicleCategory>Tractor</VehicleCategory>
</Vehicle>
<Vehicle>
<VehicleSequenceNo>2</VehicleSequenceNo>
<VehicleCategory>Tractor</VehicleCategory>
</Vehicle>
<Vehicle>
<VehicleSequenceNo>3</VehicleSequenceNo>
<VehicleCategory>Trailer</VehicleCategory>
</Vehicle>
</Vehicles>
<Policies>
<Policy>
<PrimaryAuto>
<AutoLiability>
<Exposures>
<Vehicle>
<VehicleSequenceNo>1</VehicleSequenceNo>
<CoverageProvided>
<CoverageCode>L1</CoverageCode>
<Premium>100</Premium>
</CoverageProvided>
</Vehicle>
<Vehicle>
<VehicleSequenceNo>2</VehicleSequenceNo>
<CoverageProvided>
<CoverageCode>L1</CoverageCode>
<Premium>200</Premium>
</CoverageProvided>
</Vehicle>
<Vehicle>
<VehicleSequenceNo>3</VehicleSequenceNo>
<CoverageProvided>
<CoverageCode>L1</CoverageCode>
<Premium>150</Premium>
</CoverageProvided>
</Vehicle>
</Exposures>
</AutoLiability>
<Cargo>
<Exposures>
<Vehicle>
<VehicleSequenceNo>1</VehicleSequenceNo>
<CoverageProvided>
<CoverageCode>L1</CoverageCode>
<Premium>100</Premium>
</CoverageProvided>
</Vehicle>
<Vehicle>
<VehicleSequenceNo>2</VehicleSequenceNo>
<CoverageProvided>
<CoverageCode>L1</CoverageCode>
<Premium>200</Premium>
</CoverageProvided>
</Vehicle>
<Vehicle>
<VehicleSequenceNo>3</VehicleSequenceNo>
<CoverageProvided>
<CoverageCode>L1</CoverageCode>
<Premium>150</Premium>
</CoverageProvided>
</Vehicle>
</Exposures>
</Cargo>
</PrimaryAuto>
</Policy>
</Policies>
</ExternalFulfillmentRequest>
</Root>
output should look as below
<PremiumSplit>
<AutoLiability>
<Vehicle>
<CoverageSection>AutoLiability</CoverageSection>
<CoverageCode>L1</CoverageCode>
<!-- L1 premium across CovergaeSections 100 + 200 + 150 = 450; L1 premium for VehicleCategory tractor 100 + 200 = 300; 450 / 300 = 1.5 -->
<CoveragePercent>1.5</CoveragePercent>
<VehicleCategory>Tractor</VehicleCategory>
</Vehicle>
<Vehicle>
<CoverageSection>AutoLiability</CoverageSection>
<CoverageCode>L1</CoverageCode>
<!-- L1 premium across CovergaeSections 100 + 200 + 150 = 450; L1 premium for VehicleCategory trailer 150; 450 / 150 = 3 -->
<CoveragePercent>3</CoveragePercent>
<VehicleCategory>Trailer</VehicleCategory>
</Vehicle>
</AutoLiability>
<Cargo>
<Vehicle>
<CoverageSection>Cargo</CoverageSection>
<CoverageCode>L1</CoverageCode>
<CoveragePercent>1.5</CoveragePercent>
<VehicleCategory>Tractor</VehicleCategory>
</Vehicle>
<Vehicle>
<CoverageSection>Cargo</CoverageSection>
<CoverageCode>L1</CoverageCode>
<CoveragePercent>3</CoveragePercent>
<VehicleCategory>Trailer</VehicleCategory>
</Vehicle>
</Cargo>
</PremiumSplit>
aslo xslt should perform below
1. Group covergaecode based on coverageCode, vehiclecategory per coverageSection
2. sum up premium per coverageCode
3. sum up premium per coverageCode and vehiclecategory per coverageSection
4. calculate CoveragePercent i.e. premium split percent based on point 2 and 3 mentioned above, for CoveragePercent avoid divde by 0 i.e. replace infinity with 0 else actual CoveragePercent
5. output the same coveragecode only once per vehiclecategory per coveragesection
I have written 2 versions
V1 - works as expected in online xslt test tools . But compiler throws exception as .net do not support key function under use attribute if match attribute as well deined. i.e. these 2 keys are not acceptable for the compiler
<xsl:key name="al-coverage-per-category"/> and
<xsl:key name="cg-coverage-per-category"/>
V1 Code -
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" omit-xml-declaration="yes" method="xml"/>
<xsl:key name="vehicle" match="Vehicles/Vehicle" use="VehicleSequenceNo"/>
<xsl:key name="al-coverage-per-code" match="AutoLiability/Exposures/Vehicle/CoverageProvided" use="CoverageCode"/>
<xsl:key name="al-coverage-per-category" match="AutoLiability/Exposures/Vehicle/CoverageProvided" use="key('vehicle',../VehicleSequenceNo)/VehicleCategory"/>
<xsl:key name="cg-coverage-per-code" match="Cargo/Exposures/Vehicle/CoverageProvided" use="CoverageCode"/>
<xsl:key name="cg-coverage-per-category" match="Cargo/Exposures/Vehicle/CoverageProvided" use="key('vehicle',../VehicleSequenceNo)/VehicleCategory"/>
<xsl:template match="/">
<PremiumSplit>
<xsl:apply-templates select="//VehicleCategory"/>
</PremiumSplit>
</xsl:template>
<xsl:template match="VehicleCategory">
<xsl:if test="not(preceding::VehicleCategory[.=current()])">
<xsl:if test="//Cargo/Exposures/Vehicle/CoverageProvided">
<xsl:apply-templates select="//Cargo/Exposures/Vehicle/CoverageProvided">
<xsl:with-param name="category" select="."/>
</xsl:apply-templates>
</xsl:if>
<xsl:if test="//AutoLiability/Exposures/Vehicle/CoverageProvided">
<xsl:apply-templates select="//AutoLiability/Exposures/Vehicle/CoverageProvided">
<xsl:with-param name="category" select="."/>
</xsl:apply-templates>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template match="Cargo/Exposures/Vehicle/CoverageProvided">
<xsl:param name="category"/>
<xsl:if test="(generate-id() = generate-id(key('cg-coverage-per-code',CoverageCode)[1])) and not(preceding::Vehicle[CoverageSection='Cargo' and CoverageCode=current()/CoverageCode and VehicleCategory=$category]) and key('cg-coverage-per-category',$category)[CoverageCode=current()/CoverageCode]">
<xsl:variable name="cg-total-premium-per-code" select="sum(key('cg-coverage-per-code',CoverageCode)/Premium)"/>
<xsl:variable name="cg-category-premium-per-code" select="sum(key('cg-coverage-per-category',$category)[CoverageCode=current()/CoverageCode]/Premium)"/>
<Vehicle>
<CoverageSection>Cargo</CoverageSection>
<xsl:copy-of select="CoverageCode"/>
<CoveragePercent>
<xsl:choose>
<xsl:when test="$cg-category-premium-per-code != 0">
<xsl:value-of select="$cg-total-premium-per-code div $cg-category-premium-per-code"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</CoveragePercent>
<xsl:copy-of select="$category"/>
</Vehicle>
</xsl:if>
</xsl:template>
<xsl:template match="AutoLiability/Exposures/Vehicle/CoverageProvided">
<xsl:param name="category"/>
<xsl:if test="(generate-id() = generate-id(key('al-coverage-per-code',CoverageCode)[1])) and not(preceding::Vehicle[CoverageSection='AutoLiability' and CoverageCode=current()/CoverageCode and VehicleCategory=$category]) and key('al-coverage-per-category',$category)[CoverageCode=current()/CoverageCode]">
<xsl:variable name="al-total-premium-per-code" select="sum(key('al-coverage-per-code',CoverageCode)/Premium)"/>
<xsl:variable name="al-category-premium-per-code" select="sum(key('al-coverage-per-category',$category)[CoverageCode=current()/CoverageCode]/Premium)"/>
<Vehicle>
<CoverageSection>AutoLiability</CoverageSection>
<xsl:copy-of select="CoverageCode"/>
<CoveragePercent>
<xsl:choose>
<xsl:when test="$al-category-premium-per-code != 0">
<xsl:value-of select="$al-total-premium-per-code div $al-category-premium-per-code"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</CoveragePercent>
<xsl:copy-of select="$category"/>
</Vehicle>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
where as V2 code - without using Key function,it do not generate desired output. Current it add same coveragecode for all vehicles defined. But expectation is to have only one coveragecode per vehiclecategoryin output. Please refer sample output pasted.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" omit-xml-declaration="yes" method="xml"/>
<xsl:template match="/">
<PremiumSplit>
<AutoLiability>
<xsl:apply-templates select="//AutoLiability/Exposures/Vehicle/CoverageProvided"/>
</AutoLiability>
<Cargo>
<xsl:apply-templates select="//Cargo/Exposures/Vehicle/CoverageProvided"/>
</Cargo>
</PremiumSplit>
</xsl:template>
<xsl:template match="AutoLiability/Exposures/Vehicle/CoverageProvided">
<xsl:variable name="vehicleSeqNo" select="../VehicleSequenceNo"/>
<xsl:variable name="coverageCode" select="CoverageCode"/>
<xsl:variable name="vehicleCategory" select="//Vehicles/Vehicle[VehicleSequenceNo=$vehicleSeqNo]/VehicleCategory"/>
<xsl:variable name="totalPremiumPerCode" select="sum(//AutoLiability/Exposures/Vehicle/CoverageProvided[CoverageCode=$coverageCode]/Premium)"/>
<xsl:variable name="categoryPremiumPerCode" select="sum(//AutoLiability/Exposures/Vehicle[VehicleSequenceNo=$vehicleSeqNo]/CoverageProvided[CoverageCode=$coverageCode]/Premium)"/>
<Vehicle>
<CoverageSection>AutoLiability</CoverageSection>
<xsl:copy-of select="$coverageCode"/>
<CoveragePercent>
<xsl:choose>
<xsl:when test="$categoryPremiumPerCode != 0">
<xsl:value-of select="$totalPremiumPerCode div $categoryPremiumPerCode"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</CoveragePercent>
<xsl:copy-of select="$vehicleCategory"/>
</Vehicle>
</xsl:template>
<xsl:template match="Cargo/Exposures/Vehicle/CoverageProvided">
<xsl:variable name="vehicleSeqNo" select="../VehicleSequenceNo"/>
<xsl:variable name="coverageCode" select="CoverageCode"/>
<xsl:variable name="vehicleCategory" select="//Vehicles/Vehicle[VehicleSequenceNo=$vehicleSeqNo]/VehicleCategory"/>
<xsl:variable name="totalPremiumPerCode" select="sum(//Cargo/Exposures/Vehicle/CoverageProvided[CoverageCode=$coverageCode]/Premium)"/>
<xsl:variable name="categoryPremiumPerCode" select="sum(//Cargo/Exposures/Vehicle[VehicleSequenceNo=$vehicleSeqNo]/CoverageProvided[CoverageCode=$coverageCode]/Premium)"/>
<Vehicle>
<CoverageSection>Cargo</CoverageSection>
<xsl:copy-of select="$coverageCode"/>
<CoveragePercent>
<xsl:choose>
<xsl:when test="$categoryPremiumPerCode != 0">
<xsl:value-of select="$totalPremiumPerCode div $categoryPremiumPerCode"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</CoveragePercent>
<xsl:copy-of select="$vehicleCategory"/>
</Vehicle>
</xsl:template>
</xsl:stylesheet>
Question:
a. Either rewrite V1 by removing key function
b. fix v2 version for desired output.
c. please help me with new version which can give desired output without using key function.
I spent almost 30 hours to get here. Please help.
</details>
# 答案1
**得分**: 0
Saxon .NET 旨在在 .NET 环境下运行 XSLT 2 或 3。
至于您的第一个 XSLT 1.0 版本,它看起来很复杂,但如果您将键的定义更改为
<xsl:key name="vehicle" match="Vehicles/Vehicle" use="VehicleSequenceNo"/>
<xsl:key name="al-coverage-per-code" match="AutoLiability/Exposures/Vehicle/CoverageProvided" use="CoverageCode"/>
<xsl:key name="al-coverage-per-category" match="AutoLiability/Exposures/Vehicle/CoverageProvided" use="//Vehicles/Vehicle[VehicleSequenceNo = current()/../VehicleSequenceNo]/VehicleCategory"/>
<xsl:key name="cg-coverage-per-code" match="Cargo/Exposures/Vehicle/CoverageProvided" use="CoverageCode"/>
<xsl:key name="cg-coverage-per-category" match="Cargo/Exposures/Vehicle/CoverageProvided" use="//Vehicles/Vehicle[VehicleSequenceNo = current()/../VehicleSequenceNo]/VehicleCategory"/>
Microsoft 的 XSLT 1.0 处理器实现应该能够处理这些键。我没有尝试理解或简化代码的其余部分。
<details>
<summary>英文:</summary>
Saxon .NET exists to run XSLT 2 or 3 under .NET.
As for your first XSLT 1.0 version, it looks convoluted but if you change the key definitions to
<xsl:key name="vehicle" match="Vehicles/Vehicle" use="VehicleSequenceNo"/>
<xsl:key name="al-coverage-per-code" match="AutoLiability/Exposures/Vehicle/CoverageProvided" use="CoverageCode"/>
<xsl:key name="al-coverage-per-category" match="AutoLiability/Exposures/Vehicle/CoverageProvided" use="//Vehicles/Vehicle[VehicleSequenceNo = current()/../VehicleSequenceNo]/VehicleCategory"/>
<xsl:key name="cg-coverage-per-code" match="Cargo/Exposures/Vehicle/CoverageProvided" use="CoverageCode"/>
<xsl:key name="cg-coverage-per-category" match="Cargo/Exposures/Vehicle/CoverageProvided" use="//Vehicles/Vehicle[VehicleSequenceNo = current()/../VehicleSequenceNo]/VehicleCategory"/>
Microsoft's XSLT 1.0 processor implementations should be able to handle the keys. I haven't made any attempt to understand or simplify the rest of the code.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论