Reputation: 1
I am transforming medical record data that is stored in XML with XSLT 1.0. Because these are patient records, while it is going to be okay to adjust certain text values, nothing can be removed, so I have to begin with the identity template.
I need to adjust the textual content of certain nodes based on a logical condition, such as another sibling node containing any one of up to 10 different ID numbers. I have tried filtering with predicates, and, because of its mentioning in chapter 4 of the XSLT Cookbook, I have tried applying a For-Each
with its supposedly faster ‘selecting’ method, rather than a ‘filtering’ approach based on Choose-When
logic. I’ve found the differences between Match with a predicate and using For-Each’s Select with a predicate to be negligible. I am very open to using Keys but am not quite certain how to apply them to help with my problem.
I have two main problems:
It’s crucial I find the most efficient way to match on and adjust the text of nodes meeting the conditions. We’re experiencing serious delays in processing and record retrieval times. And if I can find an optimal solution, there will be multiple places I can re-write old XSLT templates and really improve things.
I have XML that’s something like this:
<Facilities>
<Facility>XYZ</Facility>
<Records>
<!-- lots and lots of other kinds of records here A to Z, long XML before this. -->
<!-- RecordAAA #1 -->
<RecordAAA>
<Item>
<number>123</number>
<definition>123 really means this</definition>
</Item>
<ItemCategory>
<ID>AAA</ID>
<IdDescription>AAA</IdDescription>
</ItemCategory>
</RecordAAA>
<!-- RecordAAA #2 -->
<RecordAAA>
<Item>
<number>456</number>
<definition>456 really means this</definition>
</Item>
<ItemCategory>
<ID>AAA</ID>
<IdDescription>AAA</IdDescription>
</ItemCategory>
</RecordAAA>
<!-- RecordAAA #3 -->
<RecordAAA>
<Item>
<number>123</number>
<definition>123 really means this</definition>
</Item>
<ItemCategory>
<ID>AAA</ID>
<IdDescription>AAA</IdDescription>
</ItemCategory>
</RecordAAA>
<!-- lots and lots of other kinds of records here A to Z, long XML after this. -->
</Records>
</Facilities>
Desired Output: <ItemCategory> <ID>
and <ItemCategory> <IdDescription>
content changed based on RecordAAA #2’s Item/Number text being ‘456’,
while RecordAAA #1 and #3 simply copied over:
<Facilities>
<Facility>XYZ</Facility>
<Records>
<!-- lots and lots of other kinds of records here A to Z, long XML, before this. -->
<!-- RecordAAA #1 -->
<RecordAAA>
<Item>
<number>123</number>
<definition>123 really means this</definition>
</Item>
<ItemCategory>
<ID>AAA</ID>
<IdDescription>AAA</IdDescription>
</ItemCategory>
</RecordAAA>
<!-- RecordAAA #2 -->
<RecordAAA>
<Item>
<number>456</number>
<definition>456 really means this</definition>
</Item>
<!-- ID and IdDescription have been changed. -->
<ItemCategory>
<ID>BBB</ID>
<IdDescription>BBB</IdDescription>
</ItemCategory>
</RecordAAA>
<!-- RecordAAA #3 -->
<RecordAAA>
<Item>
<number>123</number>
<definition>123 really means this</definition>
</Item>
<ItemCategory>
<ID>AAA</ID>
<IdDescription>AAA</IdDescription>
</ItemCategory>
</RecordAAA>
<!-- lots and lots of other kinds of records here A to Z, long XML, after this. -->
</Records>
</Facilities>
I have XSLT templates like this, and of course this works:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Identity template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Records/RecordAAA[Item/number='456' or Item/number='789' or Item/number='101112']/ItemCategory">
<ItemCategory>
<ID>BBB</ID>
<IdDescription>BBB</IdDescription>
</ItemCategory>
</xsl:template>
</xsl:stylesheet>
But what should I do if I have facilities with more codes, and it begins to look like this:
<xsl:template match="Records/RecordAAA[Item/number='456'
or Item/number='789'
or Item/number='101112'
or Item/number='1314'
or Item/number='1516'
or Item/number='1718'
or Item/number='1920'
or Item/number='2122'
or Item/number='2324'
or Item/number='2526'
or Item/number='2728']/ItemCategory">
<ItemCategory>
<ID>BBB</ID>
<IdDescription>BBB</IdDescription>
</ItemCategory>
</xsl:template>
This still creates the desired output (as would a long series of Choose-When
conditions) but how can I get it done faster? Is there a way to really make this more efficient?
If it matters for your answer, we are using the Xalan processor and I'm stuck with version 1.0.
Thank you very much for any solid insights.
Upvotes: 0
Views: 166
Reputation: 1235
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<!-- generate-id(../..) returns the id of the grandparent of number. ie. RecordAAA -->
<xsl:key name="myKey" match="number[.='456' or .='789' or .='101112']" use="generate-id(../..)"/>
<!-- Identity template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- generate-id(..) returns the parent of ItemCategory. ie. RecordAAA-->
<xsl:template match="ItemCategory[key('myKey', generate-id(..))]">
<ItemCategory>
<ID>BBB</ID>
<IdDescription>BBB</IdDescription>
</ItemCategory>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Reputation: 2923
I'm not sure that you can make these comparisons run any faster.
You can make your code cleaner by keeping a list of facilities in a parameter or separate file.
Here's an example XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name='facilityfile'>fax.xml</xsl:param>
<xsl:variable name='facility' select='document($facilityfile)//facility/@nbr'/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!--
<xsl:template match="Records/RecordAAA[Item/number='456' or Item/number='789' or Item/number='101112']/ItemCategory">
-->
<xsl:template match='ItemCategory'>
<xsl:choose>
<xsl:when test='preceding-sibling::Item/number=$facility'>
<ItemCategory>
<ID>BBB</ID>
<IdDescription>BBB</IdDescription>
</ItemCategory>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
and the facilities fax.xml content
<xml>
<facility nbr='456'/>
<facility nbr='567'/>
<facility nbr='678'/>
<facility nbr='789'/>
</xml>
Upvotes: 1