replace xml value with sequentail numbers - xslt
Hi I have been trying to replace values in a certain tag with sequential numbers, I used position function, but it did not work.
Input xml:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header>
<User id="swarnai" />
<Request-Id id="592149819" />
<Type name="Request" />
<Application-Source version="8.1.1.10" name="Siebel" />
<Application-Destination version="1.2.2" name="EVA" />
<Outgo-Timestamp time="11:40:59" date="2015-08-04" />
<DealerCode>82536</DealerCode>
<Market>00000</Market>
</Header>
<Content>
<ClaimContext>
<ClaimControl>
<ClaimEntryFlag>3</ClaimEntryFlag>
<ClaimSaveFlag>1</ClaimSaveFlag>
</ClaimControl>
<Claim>
<DealerClaimNumber>1091871</DealerClaimNumber>
<WHC>WDD</WHC>
<FIN>2120026L020301</FIN>
<RegistrationNumber>JH07E2786</RegistrationNumber>
<FirstRegistrationDate>2012-11-29</FirstRegistrationDate>
<Mileage>14317</Mileage>
<MileageIndicator>0</MileageIndicator>
<RepairDate>2013-12-03</RepairDate>
<RegularlyMaintained>true</RegularlyMaintained>
<NoFirstRegDateInd>false</NoFirstRegDateInd>
<ClaimCurrencyId>EUR</ClaimCurrencyId>
<Taxi>false</Taxi>
<DamagePosition>
<DamageSeqNumber>1</DamageSeqNumber>
<WarrantyType>0</WarrantyType>
<DamageCode>0121504</DamageCode>
<OperationPosition>
<SeqNumber>1</SeqNumber>
<Opcode>02770501</Opcode>
<WorkingUnits>18</WorkingUnits>
<MainOperationCode>true</MainOperationCode>
<OperationText>OPERATIONS: FITTING FOR COOLANT CONNECTION TO CYLINDER</OperationText>
<PriceGroup>01</PriceGroup>
</OperationPosition>
</DamagePosition>
<DamagePosition>
<DamageSeqNumber>1</DamageSeqNumber>
<WarrantyType>0</WarrantyType>
<DamageCode>0121504</DamageCode>
<PartPosition>
<SeqNumber>1</SeqNumber>
<Quantity>10</Quantity>
<PartNumber>A6512001056</PartNumber>
<DamageCausingPart>true</DamageCausingPart>
<RetailPriceAmount>1499</RetailPriceAmount>
<Express>false</Express>
</PartPosition>
</DamagePosition>
<DamagePosition>
<DamageSeqNumber>1</DamageSeqNumber>
<WarrantyType>0</WarrantyType>
<DamageCode>0121504</DamageCode>
<PartPosition>
<SeqNumber>1</SeqNumber>
<Quantity>20</Quantity>
<PartNumber>A0009890825 10</PartNumber>
<DamageCausingPart>false</DamageCausingPart>
<RetailPriceAmount>1319</RetailPriceAmount>
<Express>false</Express>
</PartPosition>
</DamagePosition>
</Claim>
</ClaimContext>
</Content>
</Envelope>
In this xml within PartPosition/SeqNumber tag I want to replace or generate sequence of numbers in SeqNumber tag.
I tried below xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:key name="Damage_Group" match="/Envelope/Content/ClaimContext/Claim/DamagePosition" use="DamageCode" />
<xsl:template match="Claim">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
<xsl:for-each select="DamagePosition[count(. | key('Damage_Group', DamageCode)[1]) = 1]">
<xsl:sort select="DamageCode" />
<DamagePosition>
<DamageSeqNumber>
<xsl:value-of select="position()" />
</DamageSeqNumber>
<DamageCode>
<xsl:value-of select="DamageCode" />
</DamageCode>
<WarrantyType><xsl:value-of select="WarrantyType" /></WarrantyType>
<xsl:for-each select="key('Damage_Group', DamageCode)">
<xsl:copy-of select="current()/PartPosition"/>
<xsl:copy-of select="current()/OperationPosition"/>
<xsl:copy-of select="current()/SubletPosition"/>
</xsl:for-each>
</DamagePosition>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="DamagePosition">
</xsl:template>
</xsl:stylesheet>
but it is not giving me the desired output as below.
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header>
<User id="swarnai" />
<Request-Id id="592149819" />
<Type name="Request" />
<Application-Source version="8.1.1.10" name="Siebel" />
<Application-Destination version="1.2.2" name="EVA" />
<Outgo-Timestamp time="12:15:47" date="2015-08-04" />
<DealerCode>82536</DealerCode>
<Market>00000</Market>
</Header>
<Content>
<ClaimContext>
<ClaimControl>
<ClaimEntryFlag>3</ClaimEntryFlag>
<ClaimSaveFlag>1</ClaimSaveFlag>
</ClaimControl>
<Claim>
<DealerClaimNumber>1091871</DealerClaimNumber>
<WHC>WDD</WHC>
<FIN>2120026L020301</FIN>
<RegistrationNumber>JH07E2786</RegistrationNumber>
<FirstRegistrationDate>2012-11-29</FirstRegistrationDate>
<Mileage>14317</Mileage>
<MileageIndicator>0</MileageIndicator>
<RepairDate>2013-12-03</RepairDate>
<RegularlyMaintained>true</RegularlyMaintained>
<NoFirstRegDateInd>false</NoFirstRegDateInd>
<ClaimCurrencyId>EUR</ClaimCurrencyId>
<Taxi>false</Taxi>
<DamagePosition>
<DamageSeqNumber>1</DamageSeqNumber>
<DamageCode>0121504</DamageCode>
<WarrantyType>0</WarrantyType>
<OperationPosition>
<SeqNumber>1</SeqNumber>
<Opcode>02770501</Opcode>
<WorkingUnits>18</WorkingUnits>
<MainOperationCode>true</MainOperationCode>
<OperationText>OPERATIONS: FITTING FOR COOLANT CONNECTION TO CYLINDER</OperationText>
<PriceGroup>01</PriceGroup>
</OperationPosition>
<PartPosition>
<SeqNumber>1</SeqNumber>
<Quantity>10</Quantity>
<PartNumber>A6512001056</PartNumber>
<DamageCausingPart>true</DamageCausingPart>
<RetailPriceAmount>1499</RetailPriceAmount>
<Express>false</Express>
</PartPosition>
<PartPosition>
<SeqNumber>2</SeqNumber>
<Quantity>20</Quantity>
<PartNumber>A0009890825 10</PartNumber>
<DamageCausingPart>false</DamageCausingPart>
<RetailPriceAmount>1319</RetailPriceAmount>
<Express>false</Express>
</PartPosition>
</DamagePosition>
</Claim>
</ClaimContext>
</Content>
</Envelope>
Any help is much appreciated.
Thanks
Firstly, instead of doing this, which copies the elements without further changes to it (or its descendants)
<xsl:copy-of select="current()/PartPosition"/>
<xsl:copy-of select="current()/OperationPosition"/>
<xsl:copy-of select="current()/SubletPosition"/>
You should you template matching instead, to allow you to write a template later on to change the sequence number
<xsl:apply-templates select="PartPosition"/>
<xsl:apply-templates select="OperationPosition"/>
<xsl:apply-templates select="SubletPosition"/>
Now, what you could do, is write a template that matches the various Sequence numbers and counts the preceding siblings for the same DamageCode. Something like this
<xsl:template match="PartPosition/SeqNumber">
<SeqNumber>
<xsl:variable name="DamageCode" select="../../DamageCode" />
<xsl:value-of select="count(../../preceding-sibling::*[PartPosition][DamageCode = $DamageCode]) + 1" />
</SeqNumber>
</xsl:template>
You could repeat the template for OperationPosition and SubletPosition too, although this would be quite repetitive. Alternatively, have a single generic template to match all three:
<xsl:template match="SeqNumber">
<SeqNumber>
<xsl:variable name="DamageCode" select="../../DamageCode" />
<xsl:variable name="Parent" select="name(..)" />
<xsl:value-of select="count(../../preceding-sibling::*[*[name() = $Parent]][DamageCode = $DamageCode]) + 1" />
</SeqNumber>
</xsl:template>
However, this is not necessarily that efficient, as it is not utilizing the key and so would have to check back through all preceding siblings to find a match.
Another solution would be to pass the position of the current DamagePosition in a parameter, and then use that in checking the key.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:key name="Damage_Group"
match="/Envelope/Content/ClaimContext/Claim/DamagePosition"
use="DamageCode"/>
<xsl:template match="Claim">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:for-each select="DamagePosition[count(. | key('Damage_Group', DamageCode)[1]) = 1]">
<xsl:sort select="DamageCode"/>
<DamagePosition>
<DamageSeqNumber>
<xsl:value-of select="position()"/>
</DamageSeqNumber>
<DamageCode>
<xsl:value-of select="DamageCode"/>
</DamageCode>
<WarrantyType>
<xsl:value-of select="WarrantyType"/>
</WarrantyType>
<xsl:for-each select="key('Damage_Group', DamageCode)">
<xsl:apply-templates select="PartPosition">
<xsl:with-param name="Position" select="position()" />
</xsl:apply-templates>
<xsl:apply-templates select="OperationPosition">
<xsl:with-param name="Position" select="position()" />
</xsl:apply-templates>
<xsl:apply-templates select="SubletPosition">
<xsl:with-param name="Position" select="position()" />
</xsl:apply-templates>
</xsl:for-each>
</DamagePosition>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="DamagePosition/*">
<xsl:param name="Position" />
<xsl:copy>
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="Position" select="$Position" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="SeqNumber">
<xsl:param name="Position" />
<SeqNumber>
<xsl:value-of select="count(key('Damage_Group', ../../DamageCode)[position() <= $Position][*[name() = name(current()/..)]])" />
</SeqNumber>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="DamagePosition" />
</xsl:stylesheet>
Related
XSLT modify attribute value at EXACT element by passing the original value to a template
I'm struggling to get this abomination called XSLT to work. I need to get an EXACT attribute at EXACT path, pass its original value to a template and rewrite this value with the result from the template. I'm having a file like this: <?xml version="1.0" encoding="windows-1251"?> <File> <Document ReportYear="17"> ... ... </Document> </File> So I made an XSLT like this: <?xml version="1.0" encoding="windows-1251"?> <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" encoding="windows-1251" indent="yes" /> <xsl:template match="#* | node()"> <xsl:copy> <xsl:apply-templates select="#* | node()" /> </xsl:copy> </xsl:template> <xsl:template name="formatYear"> <xsl:param name="year" /> <xsl:value-of select="$year + 2000" /> </xsl:template> <xsl:template match="File/Document"> <xsl:copy> <xsl:apply-templates select="#*" /> <xsl:attribute name="ReportYear"> <xsl:call-template name="formatYear"> <xsl:with-param name="year" select="#ReportYear" /> </xsl:call-template> </xsl:attribute> </xsl:copy> <xsl:apply-templates /> </xsl:template> </xsl:stylesheet> This works fine except it closes the <Document> tag immediately and places its content immediately after itself. Also, can I address the ReportYear attribute value without repeating it twice? I tried current() but it didn't work.
If you're closing <xsl:copy> before applying templates to the remainder of the content of <Document>, then of course <Document> will be closed before the remainder of the content of <Document> appears in the output. <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" encoding="windows-1251" indent="yes" /> <xsl:template match="#* | node()"> <xsl:copy> <xsl:apply-templates select="#* | node()" /> </xsl:copy> </xsl:template> <xsl:template match="Document"> <xsl:copy> <xsl:apply-templates select="#*" /> <xsl:attribute name="ReportYear"> <xsl:value-of select="#ReportYear + 2000" /> </xsl:attribute> <xsl:apply-templates select="node()" /> </xsl:copy> </xsl:template> </xsl:stylesheet> outputs <?xml version="1.0" encoding="windows-1251"?> <File> <Document ReportYear="2017"> ... ... </Document> </File> I don't think an extra template just for adding 2000 to #ReportYear is necessary. But if you must, you can streamline the whole thing like so <xsl:template name="formatYear"> <xsl:param name="year" select="#ReportYear" /> <!-- you can define a default value --> <xsl:value-of select="$year + 2000" /> </xsl:template> and <xsl:attribute name="ReportYear"> <xsl:call-template name="formatYear" /> <!-- ...and can use it implicitly here --> </xsl:attribute>
If you need to process the contents of the Document element with apply-templates and want to keep the result of the applied templates as the children then you need to move the apply-templates inside of the copy: <xsl:template match="File/Document"> <xsl:copy> <xsl:apply-templates select="#*"/> <xsl:attribute name="ReportYear"> <xsl:call-template name="formatYear"> <xsl:with-param name="year" select="#ReportYear"/> </xsl:call-template> </xsl:attribute> <xsl:apply-templates/> </xsl:copy> </xsl:template> Not sure why you haven't simply used <xsl:template match="File/Document/#ReportYear"> <xsl:attribute name="{name()}"> <xsl:value-of select=". + 2000"/> </xsl:attribute> </xsl:template> together with the identity transformation template.
XSLT transform to .csv with re-usable generated ids
New to XSLT and I've been experimenting but want to know if this would be possible. I want to transform some XML to .csv The crux of the problem is that I want to create a numeric id for each selected element and then re-use that id for said element to link back Given the following XML: <root> <executables> <executable name="foo"> <executables> <executable name="bar"></executable> </executables> </executable> </executables> <constraints> <constraint name="baz" from="foo" to="bar"></constraint> </constraints> </root> I'd like the result to be something along the lines of: id,type,name,from,to 1,executable,foo,, 2,executable,bar,, 3,constraint,baz,1,2 Is this even possible? Here is my starting XSL: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="utf-8" indent="no"/> <xsl:template match="text()" /> <xsl:template match="/"> <xsl:text>id,type,name,from,to </xsl:text> <xsl:apply-templates/> </xsl:template> <xsl:template match="executables"> <xsl:apply-templates /> </xsl:template> <xsl:template match="constraints"> <xsl:apply-templates /> </xsl:template> <xsl:template match="executable"> <xsl:number format="1" level="any"/>,executable,<xsl:value-of select="#name" /><xsl:text>,, </xsl:text> <xsl:apply-templates /> </xsl:template> <xsl:template match="constraint"> <xsl:number format="1" level="any"/>,constraint,<xsl:value-of select="#name" />,<xsl:value-of select="#from" />,<xsl:value-of select="#to" /><xsl:text> </xsl:text> <xsl:apply-templates /> </xsl:template> </xsl:stylesheet> which gives this result: id,type,name,from,to 1,executable,foo,, 2,executable,bar,, 1,constraint,baz,foo,baz So I basically need to use the <xsl:number> matched by the attribute #name, which will be unique. Also the number isn't quite right; it counted from 1 again for the constraint match.
For the two <xsl:number format="1" level="any"/> I think you want <xsl:number count="executable | constraint" format="1" level="any"/>. For the references set up a key <xsl:key name="ref" match="executable" use="#name"/> and then instead of the <xsl:value-of select="#from" /> use e.g. <xsl:apply-templates select="key('ref', #from)" mode="number"/> and set up <xsl:template match="executable" mode="number"> <xsl:number level="any"/> </xsl:template> If the constraint elements can also be referenced then use match="executable | constraint" in the key declaration and also <xsl:number count="executable | constraint" level="any"/> in that template. And for the <xsl:value-of select="#to" /> you use <xsl:apply-templates select="key('ref', #to)" mode="number"/>. https://xsltfiddle.liberty-development.net/gWvjQgk
I would use actual generated ids, as mentioned in your title, instead of trying to produce sequential numbering: XSLT 1.0 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" encoding="utf-8" indent="no"/> <xsl:strip-space elements="*"/> <xsl:key name="exe-by-name" match="executable" use="#name" /> <xsl:template match="/root"> <xsl:text>id,type,name,from,to </xsl:text> <xsl:apply-templates/> </xsl:template> <xsl:template match="executable"> <xsl:value-of select="generate-id()" /> <xsl:text>,executable,</xsl:text> <xsl:value-of select="#name" /> <xsl:text>,, </xsl:text> <xsl:apply-templates /> </xsl:template> <xsl:template match="constraint"> <xsl:value-of select="generate-id()" /> <xsl:text>,constraint,</xsl:text> <xsl:value-of select="#name" /> <xsl:text>,</xsl:text> <xsl:value-of select="generate-id(key('exe-by-name', #from))" /> <xsl:text>,</xsl:text> <xsl:value-of select="generate-id(key('exe-by-name', #to))" /> <xsl:text> </xsl:text> <xsl:apply-templates /> </xsl:template> </xsl:stylesheet> Demo (using corrected XML): https://xsltfiddle.liberty-development.net/gWvjQgk/1
Append multiple tags on same level if a specific tag is already there
I changed now multiple times my xsl, but I didn't find the right way. Now here is what I want: I try to find now all missing Pages, which got no description. If it's missing, I want to add a description for that page, if it exist, I want to modify the Description. The String for pageX to pageXDescription is always the same. Here is my short example xml: <BOOK> <PAGE NAME='page1' VALUE='coolText'/> <PAGE NAME='Description1' VALUE='coolDescription'/> <PAGE NAME='page2' VALUE='moreText'/> <PAGE NAME='page3' VALUE='aLotMoreText'/> <PAGE NAME='Description3' VALUE='aLotMoreDescriptions'/> </BOOK> I tried it with something like that: <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- define output settings and header --> <xsl:output method="xml" indent="yes" omit-xml-declaration="no" media-type="string" encoding="ISO-8859-1" doctype-system="deftable.dtd"/> <xsl:template match="node()|#*"> <xsl:copy> <xsl:apply-templates select="node()|#*"/> </xsl:copy> </xsl:template> <xsl:template match="BOOK[PAGE/#NAME='page1']"> <xsl:copy> <xsl:call-template name="create_missing_description_pages"> <xsl:with-param name="page" select="'page1'"/> <xsl:with-param name="description" select="'Description1'"/> <xsl:with-param name="new_description" select="'newContent'"/> </xsl:call-template> </xsl:copy> </xsl:template> <xsl:template match="BOOK[PAGE/#NAME='page2']"> <xsl:copy> <xsl:call-template name="create_missing_description_pages"> <xsl:with-param name="page" select="'page2'"/> <xsl:with-param name="description" select="'Description2'"/> <xsl:with-param name="new_description" select="'newContent'"/> </xsl:call-template> </xsl:copy> </xsl:template> <xsl:template match="BOOK[PAGE/#NAME='page3']"> <xsl:copy> <xsl:call-template name="create_missing_description_pages"> <xsl:with-param name="page" select="'page3'"/> <xsl:with-param name="description" select="'Description3'"/> <xsl:with-param name="new_description" select="'newContent'"/> </xsl:call-template> </xsl:copy> </xsl:template> <!-- Function to generate missing XML Tags --> <xsl:template name="create_missing_description_pages"> <xsl:param name="page"/> <xsl:param name="description"/> <xsl:param name="new_description"/> <xsl:apply-templates select="#*|VARIABLE[#NAME=$page]/preceding-sibling::node()"/> <xsl:apply-templates select="VARIABLE[#NAME=$page]"/> <xsl:if test="not(VARIABLE/#NAME=$description)"> <xsl:element name="PAGE"> <xsl:attribute name="NAME"><xsl:value-of select="$description"/></xsl:attribute> <xsl:attribute name="VALUE"><xsl:value-of select="$new_description"/></xsl:attribute> </xsl:element> </xsl:if> <xsl:apply-templates select="VARIABLE[#NAME=$page]/following-sibling::node()"/> </xsl:template> <xsl:template match="BOOK/PAGE"> <xsl:copy> <xsl:choose> <xsl:when test="#NAME='Description1'"> <xsl:attribute name="NAME"><xsl:value-of select="#NAME"/></xsl:attribute> <xsl:attribute name="VALUE">newContent</xsl:attribute> </xsl:when> <xsl:when test="#NAME='Description2'"> <xsl:attribute name="NAME"><xsl:value-of select="#NAME"/></xsl:attribute> <xsl:attribute name="VALUE">newContent</xsl:attribute> </xsl:when> <xsl:when test="#NAME='page3Description'"> <xsl:attribute name="NAME"><xsl:value-of select="#NAME"/></xsl:attribute> <xsl:attribute name="VALUE">newContent</xsl:attribute> </xsl:when> <!-- other child items will just be copied --> <xsl:otherwise> <xsl:copy-of select="#*"/> </xsl:otherwise> </xsl:choose> </xsl:copy> </xsl:template> </xsl:stylesheet> That is what I'm expecting in all cases, if all Descriptions are missing, or if all Descriptions are available: <BOOK> <PAGE NAME='page1' VALUE='coolText'/> <PAGE NAME='Description1' VALUE='newContent'/> <PAGE NAME='page2' VALUE='moreText'/> <PAGE NAME='Description2' VALUE='newContent'/> <PAGE NAME='page3' VALUE='aLotMoreText'/> <PAGE NAME='Description3' VALUE='newContent'/> </BOOK> If there is no page3, then I want also no page Description. I hope so, that it's understandable. Thanks a lot for a hint, where my logical failure is and how to fix it. Best regards Björn
Couldn't you make this simply: XSLT 2.0 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:strip-space elements="*"/> <!-- identity transform --> <xsl:template match="#*|node()"> <xsl:copy> <xsl:apply-templates select="#*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="PAGE"> <xsl:variable name="pagenum" select="xs:integer(substring-after(#NAME, 'page'))" /> <xsl:copy-of select="."/> <PAGE NAME='Description{$pagenum}'> <xsl:attribute name="VALUE"> <xsl:choose> <xsl:when test="$pagenum=1">newContent1</xsl:when> <xsl:when test="$pagenum=2">newContent2</xsl:when> <xsl:when test="$pagenum=3">newContent3</xsl:when> </xsl:choose> </xsl:attribute> </PAGE> </xsl:template> <xsl:template match="PAGE[starts-with(#NAME, 'Description')]"/> </xsl:stylesheet>
Count Nodes and Duplicate with no values in XSLT
I am super rusty at XSLT and was wondering if someone can give me some pointers. Edit: Using XSLT 1.0 Original XML: <gic> <application> <agent> ...child nodes </agent> <client> ...child nodes </client> <bank> ...child nodes </bank> </application> </gic> I need to transform the given XML INPUT to have 5 client nodes. The input can contain 1-5 client nodes populated. I need to ensure there is always 5 in the output. In this case, one is provide, so I need to insert 4 client nodes with all child nodes. Values for all child nodes need to be empty. Output in XML
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:template match="/"> <xsl:apply-templates /> </xsl:template> <xsl:template match="application" as="node()*"> <xsl:copy> <xsl:apply-templates select="agent" /> <!-- copy existing cliens --> <xsl:apply-templates select="client" /> <!-- add new clients --> <xsl:call-template name="AddClients"> <xsl:with-param name="times" select="10 - count(client)" /> </xsl:call-template> <!-- copy banks --> <xsl:apply-templates select="bank" /> </xsl:copy> </xsl:template> <xsl:template name="AddClients"> <xsl:param name="times" select="1" /> <xsl:if test="number($times) > 0"> <!-- new element here --> <xsl:element name="client"> <xsl:attribute name="a1"> <xsl:value-of select="asas" /> </xsl:attribute> </xsl:element> <xsl:call-template name="AddClients"> <xsl:with-param name="times" select="$times - 1" /> </xsl:call-template> </xsl:if> </xsl:template> <!-- default --> <xsl:template match="#*|node()"> <xsl:copy> <xsl:apply-templates select="#*|node()" /> </xsl:copy> </xsl:template> </xsl:stylesheet>
Checking the value of nil fields
I have a requirement to remove empty and nil tags from XML. This is my code below and need help how to check for the nil fields. <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" encoding="UTF-8" indent="yes" /> <xsl:template match="/"> <xsl:call-template name="RemoveEmptyTagsMain"> <xsl:with-param name="root" select="."></xsl:with-param> </xsl:call-template> </xsl:template> <xsl:template name="RemoveEmptyTags"> <xsl:variable name="Value"> <xsl:value-of select="." /> </xsl:variable> <xsl:variable name="NodeCount" select="count(*)" /> <xsl:choose> <xsl:when test="$NodeCount > 0"> <xsl:copy> <xsl:copy-of select="#*" /> <xsl:for-each select="child::*"> <xsl:call-template name="RemoveEmptyTags" /> </xsl:for-each> </xsl:copy> </xsl:when> <xsl:otherwise> <xsl:if test="not(normalize-space($Value) = '') and not(Value=#xsl:nil)"> <xsl:copy> <xsl:copy-of select="#*" /> <xsl:value-of select="." /> </xsl:copy> </xsl:if> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="RemoveEmptyTagsMain"> <xsl:param name="root"/> <xsl:for-each select="$root"> <xsl:call-template name="RemoveEmptyTags" /> </xsl:for-each> </xsl:template> </xsl:stylesheet> This is where I am checking for empty & nillable tags. <xsl:if test="not(normalize-space($Value) = '') and not(Value=#xsl:nil)"> I have check for nillable tags like this <name xsi:nil="true">
Throw all of your code away and use this: <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" encoding="UTF-8" indent="yes" /> <xsl:template match="node() | #*"> <xsl:copy> <xsl:apply-templates select="node() | #*" /> </xsl:copy> </xsl:template> <xsl:template match="*[not(*) and normalize-space() = '']" /> </xsl:stylesheet> The above stylesheet will copy anything that is not empty. Elements that have no children (*[not(*)]) and elements that have empty text contents (*[normalize-space() = '']) are "thrown away" (i.e. not output) by the second template, everything else is copied as is by the first template. To remove all elements that do not contain anything but more nested empty elements, you can use this as the second template: <xsl:template match="*[normalize-space() = '']" />