Transform Exception. Adding Attribute after Sub-element - xslt

I have this XML:
<root>
<tab name="Detail">
<section name="mysection">
<items level="1">
<Idx_name>9</Idx_name>
<Type>mytype</Type>
<item name="myname">
<Grams um="(g)">9,0</Grams>
<Pre-infusion>Max</Pre-infusion>
</item>
<Std._Mode>On</Std._Mode>
<Price>100</Price>
</items>
</section>
</tab>
</root>
and this XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items/*">
<xsl:choose>
<xsl:when test="not(name()='item')">
<xsl:attribute name="{name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Now, what I want is:
<root>
<tab name="Detail">
<section name="mysection">
<items level="1" Idx_name="9" Type="mytype" Std._Mode="On" Price="100">
<item name="myname">9,0Max</item>
</items>
</section>
</tab>
</root>
I obtain the error: "An attribute cannot be added after a child"
Unluckily, I can not change the order of elements in the node items of my original XML
How can I do it ?
Thanks
Ivan

Make sure you process the elements first you want to transform into attributes e.g. with XSLT 2.0 where you can process sequences which have a order you can simply do
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items">
<xsl:copy>
<xsl:apply-templates select="#*, * except item, item"/>
</xsl:copy>
</xsl:template>
<xsl:template match="items/*[not(self::item)]">
<xsl:attribute name="{name()}" select="."/>
</xsl:template>
<xsl:template match="items/item">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With XSLT 1.0 you would need to spell out several apply-templates in the order you want.

Related

Replace existing element with another from an external XML

I would like to replace an element from an external XML file. Currently I have an XSLT which adds elements, but not replacing them.
Input.xml:
<all>
<item>
<sku>3850950</sku>
<name>Macbook Pro</name>
<qty>3</qty>
<img>original.jpg</img>
</item>
<item>
<sku>3859955</sku>
<name>iPhone 12</name>
<qty>9</qty>
<img>original-apple.jpg</img>
</item>
</all>
XSLT:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:key name="group" match="additem" use="sku" />
<xsl:template match="item">
<xsl:copy>
<xsl:copy-of select="*"/>
<xsl:apply-templates select="key('group', sku, document('./extra-info.xml'))"/>
</xsl:copy>
</xsl:template>
<xsl:template match="additem">
<xsl:for-each select="img">
<img>
<xsl:value-of select="."/>
</img>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Extra-info.xml:
<all>
<additem>
<sku>3850950</sku>
<img>aaaaaaa</img>
<img>bbbbbbb</img>
</additem>
<additem>
<sku>3859955</sku>
</additem>
</all>
I would like to match img elements from extra-info.xml, the common key is sku. Replace <img> in the input.xml if same exists in extra-info.xml and add all of them.
Example output:
<all>
<item>
<sku>3850950</sku>
<name>Macbook Pro</name>
<qty>3</qty>
<img>aaaaaaa</img>
<img>bbbbbbb</img>
</item>
<item>
<sku>3859955</sku>
<name>iPhone 12</name>
<qty>9</qty>
<img>original-apple.jpg</img>
</item>
</all>
AFAICT, you want to do:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="additem" match="additem" use="sku" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<xsl:variable name="additem" select="key('additem', sku, document('extra-info.xml'))"/>
<xsl:copy>
<xsl:copy-of select="* except img"/>
<xsl:choose>
<xsl:when test="$additem/img">
<xsl:copy-of select="$additem/img"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="img"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT - Update namespaces of the original xml before applying other templates

I have an xml as below:
<?xml version="1.0" encoding="UTF-8"?>
<mdb:MD_Metadata xmlns:mdb="http://standards.iso.org/iso/19115/-3/mdb/1.0"
xmlns:cit="http://standards.iso.org/iso/19115/-3/cit/1.0"
xmlns:lan="http://standards.iso.org/iso/19115/-3/lan/1.0"
xmlns:mcc="http://standards.iso.org/iso/19115/-3/mcc/1.0"
xmlns:gco="http://standards.iso.org/iso/19115/-3/gco/1.0">
<mdb:metadataIdentifier>
<mcc:MD_Identifier>
<mcc:authority>
<cit:CI_Citation>
<cit:title>
<gco:CharacterString>Old UUID</gco:CharacterString>
</cit:title>
</cit:CI_Citation>
</mcc:authority>
<mcc:code>
<gco:CharacterString>3796749d-c8a5-46ae-af11-24a977cb1d9a</gco:CharacterString>
</mcc:code>
<mcc:codeSpace>
<gco:CharacterString>urn:uuid</gco:CharacterString>
</mcc:codeSpace>
</mcc:MD_Identifier>
</mdb:metadataIdentifier>
</mdb:MD_Metadata>
I want to update the namespaces (mdb and cit to2.0) before applying other templates:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:cit1="http://standards.iso.org/iso/19115/-3/cit/1.0"
xmlns:cit="http://standards.iso.org/iso/19115/-3/cit/2.0"
xmlns:lan="http://standards.iso.org/iso/19115/-3/lan/1.0"
xmlns:mcc="http://standards.iso.org/iso/19115/-3/mcc/1.0"
xmlns:mco="http://standards.iso.org/iso/19115/-3/mco/1.0"
xmlns:mda="http://standards.iso.org/iso/19115/-3/mda/1.0"
xmlns:mdb1="http://standards.iso.org/iso/19115/-3/mdb/1.0"
xmlns:mdb="http://standards.iso.org/iso/19115/-3/mdb/2.0"
xmlns:gco="http://standards.iso.org/iso/19115/-3/gco/1.0"
xmlns:gcx="http://standards.iso.org/iso/19115/-3/gcx/1.0"
xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
exclude-result-prefixes="#all">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="mdb1:*">
<xsl:element name="{name()}" namespace="http://standards.iso.org/iso/19115/-3/mdb/2.0">
<xsl:if test="count(ancestor::*) = 0">
<xsl:call-template name="add-iso19115-3.2018-namespaces"/>
</xsl:if>
</xsl:element>
</xsl:template>
<xsl:template match="cit1:*" >
<xsl:element name="{name()}" namespace="http://standards.iso.org/iso/19115/-3/cit/2.0">
<xsl:apply-templates select="#*|*"/>
</xsl:element>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<!--Identity template that will copy every attribute, element, comment, and processing instruction to the output-->
<xsl:template match="#*|node()">
<xsl:copy copy-namespaces="no">
<!-- Including any attributes it has and any child nodes -->
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="mdb:MD_Metadata" mode="main">
<xsl:copy>
<xsl:apply-templates select="#* , node()"/>
<!-- add mdb:defaultLocale after mdb:metadataIdentifier -->
<mdb:defaultLocale>
<lan:PT_Locale id="EN">
<lan:language>
<lan:LanguageCode codeList="http://www.loc.gov/standards/iso639-2/" codeListValue="eng"/>
</lan:language>
<lan:characterEncoding>
<lan:MD_CharacterSetCode codeList="http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml#MD_CharacterSetCode" codeListValue="utf8"/>
</lan:characterEncoding>
</lan:PT_Locale>
</mdb:defaultLocale>
</xsl:copy>
</xsl:template>
<!-- replace cit:title -->
<xsl:template match="mdb:metadataIdentifier/mcc:MD_Identifier/mcc:authority/cit:CI_Citation/cit:title/gco:CharacterString/text()">
<xsl:value-of select="'UUID Test'"/>
</xsl:template>
<!-- add mcc:description -->
<xsl:template match="mdb:metadataIdentifier/mcc:MD_Identifier">
<xsl:apply-templates/>
<mcc:description>
<gco:CharacterString>Data was submitted
</gco:CharacterString>
</mcc:description>
</xsl:template>
<!-- update attribute 'codeLIst' value of mdb:metadataScope > mdb:MD_MetadataScope > mdb:resourceScope > mcc:MD_ScopeCode -->
<xsl:template match="mdb:metadataScope/mdb:MD_MetadataScope/mdb:resourceScope/mcc:MD_ScopeCode">
<xsl:if test="#codeListValue">
<xsl:attribute name="codeList">
<xsl:value-of select="'https://schemas.isotc211.org/19115/resources/Codelist/cat/codelists.xml#MD_ScopeCode'" />
</xsl:attribute>
</xsl:if>
</xsl:template>
<xsl:template name="add-iso19115-3.2018-namespaces">
<xsl:namespace name="xsi" select="'http://www.w3.org/2001/XMLSchema-instance'"/>
<xsl:namespace name="cit" select="'http://standards.iso.org/iso/19115/-3/cit/2.0'"/>
<xsl:namespace name="lan" select="'http://standards.iso.org/iso/19115/-3/lan/1.0'"/>
<xsl:namespace name="mcc" select="'http://standards.iso.org/iso/19115/-3/mcc/1.0'"/>
<xsl:namespace name="gco" select="'http://standards.iso.org/iso/19115/-3/gco/1.0'"/>
<xsl:namespace name="xlink" select="'http://www.w3.org/1999/xlink'"/>
</xsl:template>
</xsl:stylesheet>
The stylesheet above only work if the namespaces associated with mdb and cit in the original file are as below:
<mdb:MD_Metadata xmlns:mdb="http://standards.iso.org/iso/19115/-3/mdb/2.0"
xmlns:cit="http://standards.iso.org/iso/19115/-3/cit/2.0"
xmlns:lan="http://standards.iso.org/iso/19115/-3/lan/1.0"
xmlns:mcc="http://standards.iso.org/iso/19115/-3/mcc/1.0"
xmlns:gco="http://standards.iso.org/iso/19115/-3/gco/1.0">
....
How can I first update the namespaces and then apply the rest templates against the updated xml?
Perhaps the following is what you want, performing the namespace transformations and the other changes in one transformation step:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:cit1="http://standards.iso.org/iso/19115/-3/cit/1.0"
xmlns:cit="http://standards.iso.org/iso/19115/-3/cit/2.0"
xmlns:lan="http://standards.iso.org/iso/19115/-3/lan/1.0"
xmlns:mcc="http://standards.iso.org/iso/19115/-3/mcc/1.0"
xmlns:mco="http://standards.iso.org/iso/19115/-3/mco/1.0"
xmlns:mda="http://standards.iso.org/iso/19115/-3/mda/1.0"
xmlns:mdb1="http://standards.iso.org/iso/19115/-3/mdb/1.0"
xmlns:mdb="http://standards.iso.org/iso/19115/-3/mdb/2.0"
xmlns:gco="http://standards.iso.org/iso/19115/-3/gco/1.0"
xmlns:gcx="http://standards.iso.org/iso/19115/-3/gcx/1.0"
xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
exclude-result-prefixes="#all">
<xsl:output method="xml" indent="yes"/>
<!--Identity template that will copy every attribute, element, comment, and processing instruction to the output-->
<xsl:template match="#*|node()">
<xsl:copy copy-namespaces="no">
<!-- Including any attributes it has and any child nodes -->
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/mdb1:*">
<xsl:element name="mdb:{local-name()}">
<xsl:call-template name="add-iso19115-3.2018-namespaces"/>
<xsl:apply-templates select="#* , node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="mdb1:*">
<xsl:element name="mdb:{local-name()}">
<xsl:apply-templates select="#* , node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="cit1:*">
<xsl:element name="cit:{local-name()}">
<xsl:apply-templates select="#* , node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="mdb1:MD_Metadata">
<xsl:copy>
<xsl:apply-templates select="#* , node()"/>
<!-- add mdb:defaultLocale after mdb:metadataIdentifier -->
<mdb:defaultLocale>
<lan:PT_Locale id="EN">
<lan:language>
<lan:LanguageCode codeList="http://www.loc.gov/standards/iso639-2/" codeListValue="eng"/>
</lan:language>
<lan:characterEncoding>
<lan:MD_CharacterSetCode codeList="http://www.isotc211.org/2005/resources/Codelist/gmxCodelists.xml#MD_CharacterSetCode" codeListValue="utf8"/>
</lan:characterEncoding>
</lan:PT_Locale>
</mdb:defaultLocale>
</xsl:copy>
</xsl:template>
<!-- replace cit:title -->
<xsl:template match="mdb1:metadataIdentifier/mcc:MD_Identifier/mcc:authority/cit1:CI_Citation/cit1:title/gco:CharacterString/text()">
<xsl:value-of select="'UUID Test'"/>
</xsl:template>
<!-- add mcc:description -->
<xsl:template match="mdb1:metadataIdentifier/mcc:MD_Identifier">
<xsl:apply-templates/>
<mcc:description>
<gco:CharacterString>Data was submitted
</gco:CharacterString>
</mcc:description>
</xsl:template>
<!-- update attribute 'codeLIst' value of mdb:metadataScope > mdb:MD_MetadataScope > mdb:resourceScope > mcc:MD_ScopeCode -->
<xsl:template match="mdb1:metadataScope/mdb1:MD_MetadataScope/mdb1:resourceScope/mcc:MD_ScopeCode">
<xsl:if test="#codeListValue">
<xsl:attribute name="codeList">
<xsl:value-of select="'https://schemas.isotc211.org/19115/resources/Codelist/cat/codelists.xml#MD_ScopeCode'" />
</xsl:attribute>
</xsl:if>
</xsl:template>
<xsl:template name="add-iso19115-3.2018-namespaces">
<xsl:namespace name="xsi" select="'http://www.w3.org/2001/XMLSchema-instance'"/>
<xsl:namespace name="cit" select="'http://standards.iso.org/iso/19115/-3/cit/2.0'"/>
<xsl:namespace name="lan" select="'http://standards.iso.org/iso/19115/-3/lan/1.0'"/>
<xsl:namespace name="mcc" select="'http://standards.iso.org/iso/19115/-3/mcc/1.0'"/>
<xsl:namespace name="gco" select="'http://standards.iso.org/iso/19115/-3/gco/1.0'"/>
<xsl:namespace name="xlink" select="'http://www.w3.org/1999/xlink'"/>
</xsl:template>
</xsl:stylesheet>

XSLT merge multiple nodes under same parent

Input XML is
<Operations>
<ID>10</ID>
<UserArea>
<AdditionalPhantomInformation>
<PhantomItem>
<ItemCode>41341288</ItemCode>
<SubComponent>40241289</SubComponent>
<Position>1</Position>
</PhantomItem>
</AdditionalPhantomInformation>
</UserArea>
<ConsumedItem>
<LineNumber>3</LineNumber>
<ParentItem>40241288</ParentItem>
</ConsumedItem>
<UserArea>
<AdditionalPhantomInformation>
<PhantomItem>
<ItemCode>41341288</ItemCode>
<SubComponent>40241302</SubComponent>
<Position>5</Position>
</PhantomItem>
</AdditionalPhantomInformation>
</UserArea>
</Operations>
And my expected output is
<Operations>
<ID>10</ID>
<UserArea>
<AdditionalPhantomInformation>
<PhantomItem>
<ItemCode>41341288</ItemCode>
<SubComponent>40241289</SubComponent>
<Position>1</Position>
</PhantomItem>
<PhantomItem>
<ItemCode>41341288</ItemCode>
<SubComponent>40241302</SubComponent>
<Position>5</Position>
</PhantomItem>
</AdditionalPhantomInformation>
</UserArea>
<ConsumedItem>
<LineNumber>3</LineNumber>
<ParentItem>40241288</ParentItem>
</ConsumedItem>
</Operations>
I have searched various sources and tried, but i'm unable to get the xslt right. I don't know how to use xsl:for-each-group. Please help. I'm using XSLT 2.0.
You don't necessarily need xsl:for-each-group here, as all you are doing is combining specific nodes into one.
Start off with the identity template...
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Or, if you could use XSLT 3.0....
<xsl:mode on-no-match="shallow-copy"/>
Then have a template matching the first UserArea which does the combining...
<xsl:template match="UserArea[1]">
<xsl:copy>
<xsl:apply-templates select="node()|following-sibling::UserArea/node()" />
</xsl:copy>
</xsl:template>
You would then just need another template to ensure the other UserArea elements are not output in their original position.
<xsl:template match="UserArea" />
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="UserArea[1]">
<xsl:copy>
<xsl:apply-templates select="node()|following-sibling::UserArea/node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="UserArea" />
</xsl:stylesheet>
Note, if you did want to use xsl:for-each-group you would do it this way
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Operations">
<xsl:copy>
<xsl:for-each-group select="*" group-by="name()">
<xsl:choose>
<xsl:when test="current-grouping-key() = 'UserArea'">
<xsl:copy>
<xsl:apply-templates select="current-group()/node()" />
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This has the advantage of being easily extended if you had nodes other than UserArea you wished to combine.

xslt to add default value where element doesn't exist

I'm struggling with trying to test to see if an element exists. If it doesn't, I'd like to add in a default value. Here's my XML
<records>
<record>
<InstanceData>
<instance>
<FirstName>Johhny</FirstName>
<LastName>Jenkins</LastName>
<AlbumCount>3</AlbumCount>
</instance>
</InstanceData>
</record>
<record>
<InstanceData>
<instance>
<FirstName>Art</FirstName>
<LastName>Tatum</LastName>
<AlbumCount>7</AlbumCount>
</instance>
</InstanceData>
</record>
<record>
<InstanceData>
<instance>
<FirstName>Count</FirstName>
<LastName>Basie</LastName>
</instance>
</InstanceData>
</record>
</records>
I'd like to be able to copy over existing values and set any record without the Album Count element to <AlbumCount>0</AlbumCount>. This is the xslt I've been working with but I think I'm some way off the mark.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="Records">
<xsl:for-each select="node()">
<xsl:choose>
<xsl:when test="name()='AlbumCount'">
<xsl:element name="AlbumCount">
<xsl:choose>
<xsl:when test="name()='AlbumCount'">
<xsl:copy-of select=".">
</xsl:copy-of>
</xsl:when>
<xsl:otherwise>
<AlbumCount>0</AlbumCount>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select=".">
</xsl:copy-of>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Thanks for looking.
Try this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="no"/>
<!-- identity template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="instance[not(AlbumCount)]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<AlbumCount>0</AlbumCount>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Start with the identity transformation, then just handle the exception differently.
You test for the existance of an element simply with the elements name, for example:
<xsl:if test="not(AlbumCount)">
<AlbumCount>0</AlbumCount>
</xsl:if>
The simpler way to do what you want is to use the standard copy template combined with a special rule for places where AlbumCount elements need adding:
<?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"/>
<!-- Standard copy template -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!-- Special template to add AlbumCount elements where required -->
<xsl:template match="records/record/InstanceData/instance">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<xsl:if test="not(AlbumCount)">
<AlbumCount>0</AlbumCount>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Copy the attributes in XSLT

Now I have an XML file as below:
<DM Name="A DM">
<DV id="SQL:Select something from db" Name="DV 1">
<Sample aid="SQL:Select something from db" />
</DV>
<DV id="SQL:Select something from db" Name="DV 2">
<Sample aid="SQL:Select something from db" name ="DC">
good
</Sample>
</DV>
</DM>
I want to use an XSLT to transform it, there is a parameter in this tamplet to determine which DV should be transformed: if the parameter($dvIndex = 0), then just keep all the elements and attributes, just transform the attriform the attritributes with the value started with "SQL:", if ($dvindext > 0), just transform the specific DV,(remove other DV). Now I write the XSLT as below, but it miss the DM's attributes, I don't know how to copy DM's attributes. I donot know if there is better solution. XML File:
<?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"
xmlns:user="urn:my-scripts"
>
<xsl:output method="xml" indent="yes"/>
<msxsl:script language="C#" implements-prefix="user">
<![CDATA[
public string UpperCase(string value){
return value.ToUpper();
}
]]>
</msxsl:script>
<xsl:param name="dvIndex" select="2" />
<xsl:template match="DM" >
<xsl:copy>
<xsl:choose>
<xsl:when test="$dvIndex > 0">
<xsl:apply-templates select="DV[$dvIndex]"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--[starts-with(translate(substring(.,1,4),'SQL:','sql:'),'sql:')]-->
<xsl:template match="#*[user:UpperCase(substring(.,1,4))='SQL:']">
<xsl:attribute name="{name()}">
<xsl:value-of select="'parsedSQL'"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
this question is also related to my question 2# (How to only convert an XML file's attribute using XSLT, and leave the other content?)
Thanks very much!
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="dvIndex" select="2" />
<xsl:template match="DM" >
<xsl:copy>
<xsl:apply-templates select="#*|DV[$dvIndex]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*[starts-with(translate(substring(.,1,4),'SQL:','sql:'),'sql:')]">
<xsl:attribute name="{name()}">
<xsl:value-of select="'parsedSQL'"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Result:
<DM Name="A DM">
<DV id="parsedSQL" Name="DV 2">
<Sample aid="parsedSQL" name="DC">
good
</Sample>
</DV>
</DM>
With param dvIndex in 0:
<DM Name="A DM"></DM>
Note: Avoid scripting: it's not standar, it'll force to load script engine every time is used.
EDIT: If you want to process every DV when $dvIndex is 0, then this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="dvIndex" select="2" />
<xsl:template match="DM" >
<xsl:copy>
<xsl:apply-templates select="#*|DV[$dvIndex]|DV[not($dvIndex)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*[starts-with(translate(substring(.,1,4),'SQL:','sql:'),'sql:')]">
<xsl:attribute name="{name()}">
<xsl:value-of select="'parsedSQL'"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
With $dvIndex is 0, output:
<DM Name="A DM">
<DV id="parsedSQL" Name="DV 1">
<Sample aid="parsedSQL"></Sample>
</DV>
<DV id="parsedSQL" Name="DV 2">
<Sample aid="parsedSQL" name="DC">
good
</Sample>
</DV>
</DM>
The following probably does what you need. Note the additional <xsl:apply-templates select="#*" /> to copy the attributes.
<?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"
xmlns:user="urn:my-scripts">
<xsl:output method="xml" indent="yes"/>
<msxsl:script language="C#" implements-prefix="user">
<![CDATA[
public string UpperCase(string value){
return value.ToUpper();
} ]]>
</msxsl:script>
<xsl:param name="dvIndex" select="0" />
<xsl:template match="DM" >
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:choose>
<xsl:when test="$dvIndex > 0">
<xsl:apply-templates select="DV[$dvIndex]"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="DV"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--[starts-with(translate(substring(.,1,4),'SQL:','sql:'),'sql:')]-->
<xsl:template match="#*[user:UpperCase(substring(.,1,4))='SQL:']">
<xsl:attribute name="{name()}">
<xsl:value-of select="'parsedSQL'"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>