I have a .xsl file like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:exslt="http://exslt.org/common">
<xsl:template match="/>
<fo:root>
<fo:block>...</fo:block>
</fo:root>
</xsl:template>
</xsl:stylesheet>
How can I use templates to match and style the generated fo elements? For example, if I want to give my fo:table-cells red backgrounds, I'd like to be able to do
<xsl:template match="fo:table-cell">
<xsl:attribute name="background-color">red</xsl:attribute>
</xsl:template>
I found this and then tried something along the lines of
<xsl:template match="/>
<xsl:variable name="foRoot">
<fo:root>
<fo:block>...</fo:block>
</fo:root>
</xsl:variable>
<xsl:apply-templates select="exslt:node-set($foRoot)" />
</xsl:template>
but this results in a stack overflow due to endless recursion. When I try to avoid this, for example by doing
<xsl:apply-templates select="exslt:node-set($foRoot)/*" />
I get an empty document. When trying to fix that by adding
<xsl:copy-of select="$foRoot" />
right after, I don't get any errors but the table-cells still have a default white background.
If you really use an XSLT 2 processor then first of all you don't need exsl:node-set.
As for your template
<xsl:template match="fo:table-cell">
<xsl:attribute name="background-color">red</xsl:attribute>
</xsl:template>
that would match a FO table-cell but transform it into an attribute. So you rather want
<xsl:template match="fo:table-cell">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="background-color">red</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
as that adds the attribute to a shallow copy of the element and then keeps processing alive for child elements with apply-templates.
Of course you will also need to add the identity transformation template
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
to make sure the elements you don't want to change are copied through. It might be necessary to use modes to separate processing steps if the other templates you have interfere with the identity transformation.
Related
Given this XML
<dmodule>
<content>
<warningsAndCautionsRef>
<warningRef id="w001" warningIdentNumber="warning-001">
</warningRef>
<warningRef id="w002" warningIdentNumber="warning-002">
</warningRef>
<cautionRef id="c001" cautionIdentNumber="caution-001">
</cautionRef>
<cautionRef id="c002" cautionIdentNumber="caution-002">
</cautionRef>
</warningsAndCautionsRef>
<faultReporting>
<preliminaryRqmts>
<reqSafety>
<safetyRqmts cautionRefs="c001 c002" warningRefs="w001 w002"/>
</reqSafety>
</preliminaryRqmts>
</faultReporting>
</content>
</dmodule>
I would like to tokenize the attributes #cautionRefs (and #warningRefs) and then find the cautionRef element that matches its #id to the tokenized value:
<xsl:template match="#cautionRefs">
<xsl:for-each select="tokenize(.,'\s')">
<xsl:apply-templates select="//*[#id=.]"/>
</xsl:for-each>
</xsl:template>
but the apply-templates fails: Fatal error during transformation Leading '/' selects nothing: the context item is not a node. It works if I don't tokenize and use string functions instead but that is not desirable.
Desired result:
Tokenize #cautionRefs="c001 c002" (which has multiple parent elements)
So each value is passed to the <cautionRef>template that will retrieve the caution and warning statements, to be displayed in a PDF:
<xsl:apply-templates select="//*[#id='c001']"/>
<xsl:apply-templates select="//*[#id='c002']"/>
I tried using <xsl:key name="id" match="*" use="#id"/> with
<xsl:for-each select="key('id',tokenize(.,'\s'))">
but the for-each is blank.
The above apply-templates will match with this <cautionRef> template, which retrieves the caution and warning statements correctly. I just need help with the context of the #cautionRefs template:
<xsl:template match="cautionRef">
<xsl:variable name="IdentNumber" select="#cautionIdentNumber"/>
<xsl:apply-templates select="//cautionSpec[cautionIdent/#cautionIdentNumber=$IdentNumber]"/>
</xsl:template>
You could use a variable and use that for context:
<xsl:template match="#cautionRefs|#warningRefs">
<xsl:variable name="ctx" select="/"/>
<xsl:for-each select="tokenize(.,'\s')">
<xsl:apply-templates select="$ctx//*[#id=.]"/>
</xsl:for-each>
</xsl:template>
but I would use a key like you hinted at (updated to include context based on comments)...
<xsl:key name="by_id" match="*[#id]" use="#id"/>
<xsl:variable name="root" select="/"/>
<xsl:template match="#cautionRefs|#warningRefs">
<xsl:for-each select="tokenize(.,'\s')">
<xsl:apply-templates select="key('by_id',.,$root)"/>
</xsl:for-each>
</xsl:template>
Here's a full working example. NB it's best to have this level of detail in the actual question; i.e. a sample input file, the XSLT, and output, along with an example of what you want the output to look like.
Input:
<test>
<safetyRqmts cautionRefs="c001 c002" warningRefs="w001"/>
<cautionRef id="c001" cautionIdentNumber="caution-001"/>
<cautionRef id="c002" cautionIdentNumber="caution-001"/>
</test>
Stylesheet:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="*|#*">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:key name="by_id" match="*[#id]" use="#id"/>
<xsl:template match="#cautionRefs|#warningRefs">
<xsl:apply-templates select="key('by_id', tokenize(.))"/>
</xsl:template>
</xsl:stylesheet>
Output:
<test>
<safetyRqmts><cautionRef id="c001" cautionIdentNumber="caution-001"/><cautionRef id="c002" cautionIdentNumber="caution-001"/></safetyRqmts>
<cautionRef id="c001" cautionIdentNumber="caution-001"/>
<cautionRef id="c002" cautionIdentNumber="caution-001"/>
</test>
I want an xml attribute one level up in an xml structure.
As for requested I show a more detailed example:
<items>
<kitchen>
<furnitures>
<chairs type="wood">
<chair_1 color="green" legs="4"/>
</chairs>
<tables type="stone">
</tables>
</furnitures>
</kitchen>
</items>
And I want to output this:
<items>
<kitchen>
<furnitures>
<chairs type="wood"/>
<chair_1 color="green" legs="4"/>
<tables type="stone">
</tables>
</furnitures>
</kitchen>
</items>
As you see I move char_1 to under from under
<xsl:template match="node()">
<xls:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="*"/>
<xsl:apply-templates select="text()">
</xsl:copy>
</xsl:template>
<xsl:template match="/items/kitchen/furnitures/chairs">
<xsl:choose>
<xsl:when test="chair_1">
<xsl:copy>
<xsl:apply-templates select="child::node()[not(self:chair_1)]|#*|text()"/>
</xsl:copy>
<xsl:apply-templates select="chair_1"/>
</xsl:when>
<!----- edit -->
<xsl:otherwise>
<xsl:copy>
<xls:apply-templates select ="#*|node()"/>
</xsl:copy>
<xsl:apply-templates select="settings"/>
<xsl:text>
</xsl:text>
<chair_1 color="green" legs="4"/>
</xls:otherwise>
</xls:choose>
</xsl:template>
So, my main problem is, that my copy don't contain line-breaks.
Please keep in mind, I am using PHP:Xsltproc, on my dev-comp the indentation works fine, but with PHP's xsltproc it isn't fine, and drops the line breaks.
so the output like this:
<items>
<kitchen>
<furnitures>
<chairs type="wood"/><chair_1 color="green" legs="4"/>
<tables type="stone">
</tables>
</furnitures>
</kitchen>
</items>
Which is fine, but not correctly indented.
(Disclaimer: there could be some typos, as this is not the original XML, and of course I am using the required stylsheet, version, phpversion, xml version tags, and of course my output method is xml and indent="yes" )
UPDATE:
when I have the second "WHEN", (in case there is no chair_1) I want to "paste" it into the code. But the indentation fails, it makes the whole copy into one line. What could be the problem?
The templates you present do not effect the transformation you say they do, at least not by themselves. In fact, they are not even valid XSL.
After the obvious syntax errors are corrected, the resulting template matching node() explicitly rearranges whitespace around elements (where it is not stripped) and does nothing effective at preserving attributes. You seem to intend for it to be an identity transform, but the conventional identity transform goes like this:
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
Observe in particular that the node() test matches both elements and text nodes (and comments and processing instructions), but not attributes, and that if you do not want to rearrange an element's text nodes relative to its child elements then you must transform them all via the same xsl:apply-templates directive.
Additionally, do note that in many XML applications, whitespace-only runs of text separating tags are insignificant. I don't see any reason to think that your particular application is among the exceptions, so you really ought to ask yourself is "does it matter?"
Supposing that it does matter -- e.g. because you want improved human readability even though the XML is primarily meant to be consumed by a computer program that doesn't care about the indentation -- you should consider letting your XSL processor provide indentation for you. To do this, start by stripping all the insignificant whitespace from the input document:
<xsl:strip-space elements="*"/>
and follow up by asking the processor to provide hierarchical indentation for you:
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
Both of those directives have global effect, and they need to appear as direct children of the xsl:stylesheet or xsl:transform element. Here's the cleaned up and updated version:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/items/kitchen/furnitures/chairs">
<xsl:choose>
<xsl:when test="chair_1">
<xsl:copy>
<!-- also simplified the 'select' expression below: -->
<xsl:apply-templates select="node()[not(self::chair_1)]|#*"/>
</xsl:copy>
<xsl:apply-templates select="chair_1"/>
</xsl:when>
<xsl:otherwise>
BUNCH of code there if we don't have chair_1
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
suppose I have following XML as source:
<ns0:msg xmlns:ns0="namespace0">
<ns0:hdr a_lot_of_attrs="value">
some nodes...
</ns0:hdr>
<ns0:body>
<ns0:data a_lot_of_attrs="value">
<ns1:purchase_order xmlns:ns1="namespace1">some nodes...</ns1:purchase_order>
</ns0:data>
</ns0:body>
</ns0:msg>
And I need following XML as the result:
<a:msg xmlns:a="namespace0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<a:hdr a_lot_of_attrs="value">
some nodes...
</a:hdr>
<a:body>
<a:data a_lot_of_attrs="value">
<b:purchase_order
xsi:schemaLocation="filelocation"
xmlns:b="namespace1"
xmlns:c="namespace2">some nodes...</b:purchase_order>
</a:data>
</a:body>
</a:msg>
Basically I need just to replace the namespace prefix ns0 into a and ns1 into b. Further more, the root element <a:msg> as well as the <b:purchase_order> need to be added by some additional attributes.
My attemp is by using following XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="namespace0"
xmlns:ns1="namespace1"
xmlns:a="namespace0"
xmlns:b="namespace1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="ns0 ns1">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<a:msg xmlns:msg="namespace1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:copy>
<xsl:apply-templates select="ns0:msg/*"/>
</xsl:copy>
</a:msg>
</xsl:template>
<xsl:template match="/ns0:msg/ns0:body/ns0:data/ns1:purchase_order">
<b:purchase_order xsi:schemaLocation="filelocation" xmlns:c="namespace2">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</b:purchase_order>
</xsl:template>
<xsl:template match="ns0:*">
<xsl:element name="a:{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="ns1:*">
<xsl:element name="b:{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
It works fine so far, except the node <purchase_order> has been populated 2 times:
<a:msg xmlns:a="namespace0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<a:hdr a_lot_of_attrs="value">
some nodes...
</a:hdr>
<a:body>
<a:data a_lot_of_attrs="value">
<b:purchase_order xmlns:b="namespace1" xmlns:c="namespace2" xsi:schemaLocation="filelocation">
<ns1:purchase_order xmlns:ns0="namespace0" xmlns:ns1="namespace1">some nodes...</ns1:purchase_order>
</b:purchase_order>
</a:data>
</a:body>
</a:msg>
I tried several times by tweaking the second <xsl:template> but could not get it right. Would someone pls advise where I get wrong here and how can I get this done?
Thanks a lot.
You current template that matches ns1:purchase_order contains an xsl:copy as well as the creation of the new b:purchase_order. Therefore you are copying the old node as well as creating a new one.
You can remove the xsl:copy from the template, like so:
<xsl:template match="ns1:purchase_order">
<b:purchase_order xsi:schemaLocation="filelocation" xmlns:c="namespace2">
<xsl:apply-templates select="node()"/>
</b:purchase_order>
</xsl:template>
Note that you don't necessarily have to specify the full path in the template match. You would only really need to do this if you had a second ns1:purchase_order in the XML, at a different location, that you didn't want to match.
Given the following XML document:
<dialog>
<speech speaker="Robert">
<line>"Once more into the breach", he said.</line>
I can't believe him. I just <emphasis>can't</emphasis> believe him!
</speech>
</dialog>
I'd like to try and capture eveything within speech that isn't in a line already and wrap it in a line, however I need to capture any other elements along with it (eg. the emphasis in the example above).
The result I'd like to achieve is:
<dialog>
<speech speaker="Robert">
<line>"Once more into the breach", he said.</line>
<line>I can't believe him. I just <emphasis>can't</emphasis> believe him!</line>
</speech>
</dialog>
I'm using libxslt and libxml, so I'm stuck with XSLT 1.0.
One way to approach that in XSLT 1.0 is through sibling recursion, as outlined in the following example:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="speech">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="node()[1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="speech/line">
<xsl:call-template name="identity"/>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="speech/node()[not(self::line)
and (not(preceding-sibling::node())
or
preceding-sibling::node()[1][self::line])]">
<xsl:if test="normalize-space() or following-sibling::node()[1][not(self::line)]">
<line>
<xsl:call-template name="identity"/>
<xsl:apply-templates select="following-sibling::node()[1][not(self::line)]"/>
</line>
</xsl:if>
<xsl:apply-templates select="following-sibling::line[1]"/>
</xsl:template>
<xsl:template match="speech/node()[not(self::line)
and preceding-sibling::node()[1][not(self::line)]]">
<xsl:call-template name="identity"/>
<xsl:apply-templates select="following-sibling::node()[1][not(self::line)]"/>
</xsl:template>
</xsl:stylesheet>
With that stylesheet an input sample like
<dialog>
<speech speaker="Robert">
<line>"Once more into the breach", he said.</line>
I can't believe him. I just <emphasis>can't</emphasis> believe him!
</speech>
<speech speaker="Foo">This is a test.
<line>This line is wrapped and should be copied unchanged.</line>
<em>This</em> needs to be <it>wrapped</it>.
</speech>
<speech speaker="Bar"> <em>This</em> should be wrapped.
<line>This line is wrapped and should be copied unchanged.</line>
<it>Test</it>
</speech>
</dialog>
is transformed into
<dialog>
<speech speaker="Robert"><line>"Once more into the breach", he said.</line><line>
I can't believe him. I just <emphasis>can't</emphasis> believe him!
</line></speech>
<speech speaker="Foo"><line>This is a test.
</line><line>This line is wrapped and should be copied unchanged.</line><line>
<em>This</em> needs to be <it>wrapped</it>.
</line></speech>
<speech speaker="Bar"><line> <em>This</em> should be wrapped.
</line><line>This line is wrapped and should be copied unchanged.</line><line>
<it>Test</it>
</line></speech>
</dialog>
I have a XSL that needs to filter out specific data found in the XML.
Somewhere in my XML there will be a node like:
<id root="2.16.840.1.113883.3.51.1.1.6.1" extension="9494949494949" />
The XSL I have below deletes the extension node and adds a nullFlavor="MSK" to the node.
What I need to do now, is take the value from the extension node, and search the entire XML document for that value, and replace it with **.
But I'm not sure how to take the extension attribute, and find all instances of that value in the XML (they could be burried in text and inside attributes) and turn them into ** (4 *).
The example below is just an example. I cannot hard code the XSL to look at specific nodes, it needs to look through all text / attribute text in the xml (reason for this is there are 5+ different versions of XML that this will be applied to).
I need to find the Extension in the node, then replace (delete really) that value from the rest of the XML. I'm looking for a 1 solution fits all messages, so a global search->wipe of the Extension value.
Example:
<identifiedPerson classCode="IDENT">
<id root="2.16.840.1.113883.3.51.1.1.6.1" extension="9494949494949" displayable="true" />
<addr use="PHYS">
<city>KAMLOOPS</city>
<country>CA</country>
<postalCode>V1B3C1</postalCode>
<state>BC</state>
<streetAddressLine>1A</streetAddressLine>
<streetAddressLine>2A</streetAddressLine>
<streetAddressLine>9494949494949</streetAddressLine>
<streetAddressLine>4A</streetAddressLine>
</addr>
<note text="9494949494949 should be stars"/>
Should be (The below XSLT already masks the extension in the node with the matching OID).
<identifiedPerson classCode="IDENT">
<id root="2.16.840.1.113883.3.51.1.1.6.1" nullFlavor="MSK" displayable="true" />
<addr use="PHYS">
<city>KAMLOOPS</city>
<country>CA</country>
<postalCode>V1B3C1</postalCode>
<state>BC</state>
<streetAddressLine>1A</streetAddressLine>
<streetAddressLine>2A</streetAddressLine>
<streetAddressLine>****</streetAddressLine>
<streetAddressLine>4A</streetAddressLine>
</addr>
<note text="**** should be stars"/>
Any help would be appreciated.
I am able to use XSL 2.0
I have the current XSL.IT works fine. It matches any tag where the root is '2.16.840.1.113883.3.51.1.1.6.1', kills all attributes and adds a nullFlavor="MSK". However, this will not search the entire XML for that same #.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="attrToKeep" select="'root'" />
<xsl:template match="* | node()">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="#*">
<xsl:choose>
<xsl:when test="../#root = '2.16.840.1.113883.3.51.1.1.6.1'">
<xsl:copy-of select=".[contains($attrToKeep, name())]" />
<xsl:attribute name="nullFlavor">MSK</xsl:attribute>
<!-- Need some way to use the value found in this node and hide the extension -->
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Any help would be appreciated.
Thanks,
Try using a variable to hold the value of the text to be replaced. Like this:
<xsl:variable
name="rootVar"
select="//*[#root = '2.16.840.1.113883.3.51.1.1.6.1']/#extension" />
And then you should just be able to use the replace function to replace them.
<xsl:template match="'//#*' | text()">
<xsl:sequence select="replace(., $rootVar, '****')"/>
</xsl:template>
The XSLT 2.0 stylesheet
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:param name="replacement" select="'****'"/>
<xsl:param name="new" select="'MKS'"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="identifiedPerson">
<xsl:copy>
<xsl:apply-templates select="#* , node()">
<xsl:with-param name="to-be-replaced" select="id/#extension" tunnel="yes"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="identifiedPerson//text()">
<xsl:param name="to-be-replaced" tunnel="yes"/>
<xsl:sequence select="replace(., $to-be-replaced, $replacement)"/>
</xsl:template>
<xsl:template match="identifiedPerson//#*">
<xsl:param name="to-be-replaced" tunnel="yes"/>
<xsl:attribute name="{name()}" namespace="{namespace-uri()}" select="replace(., $to-be-replaced, $replacement)"/>
</xsl:template>
<xsl:template match="identifiedPerson/id">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="nullFlavor" select="$new"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="identifiedPerson/id/#extension"/>
</xsl:stylesheet>
transforms
<identifiedPerson classCode="IDENT">
<id root="2.16.840.1.113883.3.51.1.1.6.1" extension="9494949494949" displayable="true" />
<addr use="PHYS">
<city>KAMLOOPS</city>
<country>CA</country>
<postalCode>V1B3C1</postalCode>
<state>BC</state>
<streetAddressLine>1A</streetAddressLine>
<streetAddressLine>2A</streetAddressLine>
<streetAddressLine>9494949494949</streetAddressLine>
<streetAddressLine>4A</streetAddressLine>
</addr>
<note text="9494949494949 should be stars"/>
</identifiedPerson>
with Saxon 9.4 into
<?xml version="1.0" encoding="UTF-8"?><identifiedPerson classCode="IDENT">
<id root="2.16.840.1.113883.3.51.1.1.6.1" displayable="true" nullFlavor="MKS"/>
<addr use="PHYS">
<city>KAMLOOPS</city>
<country>CA</country>
<postalCode>V1B3C1</postalCode>
<state>BC</state>
<streetAddressLine>1A</streetAddressLine>
<streetAddressLine>2A</streetAddressLine>
<streetAddressLine>****</streetAddressLine>
<streetAddressLine>4A</streetAddressLine>
</addr>
<note text="**** should be stars"/>
</identifiedPerson>
So for the sample it solves that problem I think. I am not sure whether there can be more context around that sample and whether you want to change values outside of the identifiedPerson element as well or don't want to change them (which above stylesheet does). If other elements also need to be changed consider to post longer input and wanted result samples to illustrate and also explain what determines the node where the value to be replaced is found.
[edit]
Based on your comment I adapted the stylesheet, it now has a parameter to pass in a id (e.g. 2.16.840.1.113883.3.51.1.1.6.1), then it looks for an element of any name with a root attribute having that passed in id value and replaces the extension attribute value found in all attributes and all text nodes found in the document. Furthermore a nullFlavor attribute is added to the element with the id and its extension attribute is removed.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:param name="root-id" select="'2.16.840.1.113883.3.51.1.1.6.1'"/>
<xsl:variable name="to-be-replaced" select="//*[#root = $root-id]/#extension"/>
<xsl:param name="replacement" select="'****'"/>
<xsl:param name="new" select="'MKS'"/>
<xsl:template match="comment() | processing-instruction()">
<xsl:copy/>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="#* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:sequence select="replace(., $to-be-replaced, $replacement)"/>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}" select="replace(., $to-be-replaced, $replacement)"/>
</xsl:template>
<xsl:template match="*[#root = $root-id]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="nullFlavor" select="$new"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#root = $root-id]/#extension"/>
</xsl:stylesheet>