Преобразование XSLT: группировка кодов покрытия по категориям транспортных средств и расчет распределения страховых взносов
Группировка в XSLT
Необходимо создать XSLT для приведенного ниже XML-запроса.
XSLT должен сделать следующее:
- Сгруппируйте коды покрытия по категориям транспортных средств.
- Суммируйте премию по коду покрытия
- Суммируйте премию по коду покрытия и категории транспортного средства.
- Получите категорию транспортного средства на основе порядкового номера транспортного средства.
- рассчитать процент разделения на основе значений, полученных из 2 и 3 точек.
Входной XML:
<Root>
<Request>
<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>
<Liability>
<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>
<CoverageProvided>
<CoverageCode>UI</CoverageCode>
<Premium>140</Premium>
</CoverageProvided>
</Vehicle>
</Exposures>
</Liability>
</PrimaryAuto>
</Policy>
</Policies>
</Request>
</Root>
Ожидаемый результат:
<PremiumSplit>
<Liability>
<Vehicle>
<CoverageSection>Liability</CoverageSection>
<CoverageCode>L1</CoverageCode>
<!-- 100 + 200 + 150 = 450 (sum of all L1 code premium) ,
100 + 200 = 300 (sum of Tractor L1 premium) ,
450 / 300 = 1.5 -->
<CoveragePercent>1.5</CoveragePercent>
<VehicleCategory>Tractor</VehicleCategory>
</Vehicle>
<Vehicle>
<CoverageSection>Liability</CoverageSection>
<CoverageCode>L1</CoverageCode>
<!-- 100 + 200 + 150 = 450 (sum of all L1 code premium) ,
150 (Trailer L1 premium) ,
450 / 150 = 3 -->
<CoveragePercent>3</CoveragePercent>
<VehicleCategory>Trailer</VehicleCategory>
</Vehicle>
<Vehicle>
<CoverageSection>Liability</CoverageSection>
<CoverageCode>UI</CoverageCode>
<!-- 140 (Trailer UI premium) / 140 (Trailer UI premium) = 1 -->
<CoveragePercent>1</CoveragePercent>
<VehicleCategory>Trailer</VehicleCategory>
</Vehicle>
</Liability>
</PremiumSplit>
Я попробовал foreach для Root/Request/Policies/Policy/PrimaryAuto/Liability/Exposures/Vehicle/CoverageProvided и извлек CoverageCode и VehicleSequenceNumber.
Теперь мне нужно получить категорию транспортного средства для извлеченного VehicleSequenceNumber из Root/Request/Vehicles/Vehicle.
также необходимо найти повторяющийся код и выполнить добавление премии и расчет процентов.
2 ответа
Использование XSLT 3 (поддерживается текущими редакциями Saxon на платформах Java, .NET, Python и SaxonJS в браузере):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:mode on-no-match="shallow-skip"/>
<xsl:output indent="yes"/>
<xsl:key name="vehicle-category" match="Vehicles/Vehicle/VehicleCategory" use="../VehicleSequenceNo"/>
<xsl:template match="Liability">
<xsl:copy>
<xsl:for-each-group select="Exposures/Vehicle/CoverageProvided" group-by="CoverageCode">
<xsl:variable name="code-sum" select="sum(current-group()/Premium)"/>
<xsl:variable name="coverage-code" select="current-grouping-key()"/>
<xsl:for-each-group select="current-group()" group-by="key('vehicle-category', ../VehicleSequenceNo)">
<Vehicle>
<CoverageSection>Liability</CoverageSection>
<CoverageCode>{$coverage-code}</CoverageCode>
<CoveragePercent>{$code-sum div sum(current-group()/Premium)}</CoveragePercent>
<VehicleCategory>{current-grouping-key()}</VehicleCategory>
</Vehicle>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<PremiumSplit>
<xsl:apply-templates/>
</PremiumSplit>
</xsl:template>
</xsl:stylesheet>
Следующее решение XSLT 1.0 использует ключи (с понятными именами) для определения покрытий по коду и категории и сравниваетgenerate-id()
значения, чтобы выбрать только первое покрытие для данного кода или комбинации кода и категории.
Это может больше не работать должным образом, если входной XML содержит более одного<Exposures>
элемент. Для полного решения потребуется полная спецификация значения элементов входного XML. Но данное решение может послужить отправной точкой.
Более элегантные решения возможны с XSLT 2.0 или 3.0, если вы можете их использовать.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:key name="category-by-vehicle" match="VehicleCategory"
use="../VehicleSequenceNo" />
<xsl:key name="coverages-per-code" match="CoverageProvided"
use="CoverageCode" />
<xsl:key name="coverages-per-code-and-category" match="CoverageProvided"
use="concat(CoverageCode,' ',key('category-by-vehicle',../VehicleSequenceNo))" />
<xsl:template match="/">
<PremiumSplit>
<Liability>
<!-- Process only the first <CoverageProvided> with a given <CoverageCode>. -->
<xsl:apply-templates select="descendant::CoverageProvided
[generate-id()=generate-id(key('coverages-per-code',CoverageCode)[1])]"
mode="per-code" />
</Liability>
</PremiumSplit>
</xsl:template>
<xsl:template match="CoverageProvided" mode="per-code">
<!-- Process only the first <CoverageProvided> with the current <CoverageCode>
and a given <VehicleCategory>. -->
<xsl:apply-templates select="key('coverages-per-code',CoverageCode)
[generate-id()=generate-id(key('coverages-per-code-and-category',
concat(CoverageCode,' ',key('category-by-vehicle',../VehicleSequenceNo)))[1])]"
mode="per-code-and-category" />
</xsl:template>
<xsl:template match="CoverageProvided" mode="per-code-and-category">
<xsl:variable name="category" select="key('category-by-vehicle',../VehicleSequenceNo)" />
<!-- Compute the sums of <Premium>s described by steps 2 and 3. -->
<xsl:variable name="step2"
select="sum(key('coverages-per-code',CoverageCode)/Premium)" />
<xsl:variable name="step3"
select="sum(key('coverages-per-code-and-category',
concat(CoverageCode,' ',$category))/Premium)" />
<Vehicle>
<CoverageSection>Liability</CoverageSection>
<xsl:copy-of select="CoverageCode" />
<CoveragePercent>
<xsl:value-of select="$step2 div $step3" />
</CoveragePercent>
<xsl:copy-of select="$category" />
</Vehicle>
</xsl:template>
</xsl:stylesheet>