Here is my xslt:
This one works, but only if hardcode '1,2,'
<xsl:template match="row[contains('1,2,',concat(position(),','))]"
working xslt:
<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="positions"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="row[contains('1,2,',concat(position(),','))]" name="skiprow"/>
</xsl:stylesheet>
But I want to pass the position values as parameter. But it doesn't work.
I checked the value of the parameter by adding a line, the parameter is good.
<xsl:value-of select="$positions"/>
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
Non-working xslt:
<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="positions"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="row[contains('$positions',concat(position(),','))]" name="skiprow"/>
</xsl:stylesheet>
Sample Xml:
<root>
<row>
<column1>7004275</column1>
<column2>NUVCFDK</column2>
</row>
<row>
<column1>1001459</column1>
<column2>CAN</column2>
<column3>12</column3>
<column4>646.80</column4>
<column5>23-06-2009</column5>
<column6>31-12-2009</column6>
<column7/>
</row>
<row>
<column1>1001461</column1>
<column2>CAN</column2>
<column3>1</column3>
<column4>9.50</column4>
<column5>23-06-2009</column5>
<column6>31-12-2009</column6>
<column7/>
</row>
</root>
Non-working xslt:
<xsl:template match="row[contains('$positions',concat(position(),','))]" name="skiprow"/>
In XSLT 1.0 a match pattern is forbidden to contain a variable/parameter reference.
Use instead:
<xsl:template match="row>
<xsl:if test=
"not(contains($vPositions, concat(position(),',')))">
<xsl:call-template name="identity"/>
<xsl:if>
</xsl:template>
The complete transformation becomes:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="vPositions" select="'1,2,'"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="row">
<xsl:if test=
"not(contains($vPositions, concat(position(),',')))">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
and when this transformation is applied on the provided XML document:
<root>
<row>
<column1>7004275</column1>
<column2>NUVCFDK</column2>
</row>
<row>
<column1>1001459</column1>
<column2>CAN</column2>
<column3>12</column3>
<column4>646.80</column4>
<column5>23-06-2009</column5>
<column6>31-12-2009</column6>
<column7/>
</row>
<row>
<column1>1001461</column1>
<column2>CAN</column2>
<column3>1</column3>
<column4>9.50</column4>
<column5>23-06-2009</column5>
<column6>31-12-2009</column6>
<column7/>
</row>
</root>
the wanted, correct result is produced (rows 1 and 2 "deleted"):
<root>
<row>
<column1>1001461</column1>
<column2>CAN</column2>
<column3>1</column3>
<column4>9.50</column4>
<column5>23-06-2009</column5>
<column6>31-12-2009</column6>
<column7/>
</row>
</root>
Do note, however, that your conditions aren't strong enough -- a parameter value of "11,13," will delete four rows -- 1, 11, 3 and 13.
A good condition to use is:
not(contains($vPositions, concat(',',position(),',')))
That means that the parameter must start and end with the comma character.
Here is the complete, corrected transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="vPositions" select="',1,2,'"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="row">
<xsl:if test=
"not(contains($vPositions, concat(',',position(),',')))">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Related
I would like to rename and move node one level up if node value is a certain value.
So far I wrote this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="stock/images/image[main_image = 1]">
<primary>
<xsl:apply-templates />
</primary>
</xsl:template>
</xsl:stylesheet>
Input:
<root>
<stock>
<code>Apple</code>
<images>
<image>
<img_file>1.jpg</img_file>
<main_image>1</main_image>
</image>
<image>
<img_file>2.jpg</img_file>
<main_image>0</main_image>
</image>
</images>
</stock>
</root>
Output:
<root>
<stock>
<code>Apple</code>
<images>
<primary>
<img_file>1.jpg</img_file>
<main_image>1</main_image>
</primary>
<image>
<img_file>2.jpg</img_file>
<main_image>0</main_image>
</image>
</images>
</stock>
</root>
It is almost good, but I would like to move <primary> node one level up, outside of <images>, like:
<root>
<stock>
<code>Apple</code>
<primary>
<img_file>1.jpg</img_file>
<main_image>1</main_image>
</primary>
<images>
<image>
<img_file>2.jpg</img_file>
<main_image>0</main_image>
</image>
</images>
</stock>
</root>
One way would be
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="stock[images[image[main_image = 1]]]">
<xsl:copy>
<xsl:apply-templates select="#* | *[not(self::images)]"/>
<xsl:apply-templates select="images/image[main_image = 1]"/>
<xsl:apply-templates select="images"/>
</xsl:copy>
</xsl:template>
<xsl:template match="images">
<xsl:copy>
<xsl:apply-templates select="#* | node()[not(self::image[main_image = 1])]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="stock/images/image[main_image = 1]">
<primary>
<xsl:apply-templates />
</primary>
</xsl:template>
</xsl:stylesheet>
How about:
XSLT 1.0
<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"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="stock">
<xsl:copy>
<xsl:apply-templates select="code"/>
<primary>
<xsl:apply-templates select="images/image[main_image = 1]/*"/>
</primary>
<images>
<xsl:apply-templates select="images/image[not(main_image = 1)]"/>
</images>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is assuming a primary image always exists. Otherwise you would need to do:
XSLT 1.0
<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"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="stock">
<xsl:copy>
<xsl:apply-templates select="code"/>
<xsl:apply-templates select="images/image[main_image = 1]" mode="primary"/>
<images>
<xsl:apply-templates select="images/image[not(main_image = 1)]"/>
</images>
</xsl:copy>
</xsl:template>
<xsl:template match="image" mode="primary">
<primary>
<xsl:apply-templates/>
</primary>
</xsl:template>
</xsl:stylesheet>
I have this XML:
<xml>
<row>
<image><![CDATA[javascript: open_window_zoom('http://example.com/image.php?image=/images/test/example.png&pID=46391&download=noid_90.png&name=Test name', 975, 366);]]></image>
<quantity>0</quantity>
</row>
<row>
<image><![CDATA[javascript: open_window_zoom('http://example.com/image.php?image=/images/test/another.png&pID=06395&download=anotherfile.png&name=Test name', 975, 366);]]></image>
<quantity>0</quantity>
</row>
</xml>
It is possible to extract pID=NUMBERHERE&download=FILENAMEHERE.png (and add new url before it)from <image> element?
The output should be like this:
<xml>
<row>
<image>http://newurl.com/pID=46391&download=noid_90.png</image>
<quantity>0</quantity>
</row>
<row>
<image>http://newurl.com/pID=06395&download=anotherfile.png</image>
<quantity>0</quantity>
</row>
</xml>
I tried some things, but I can't get the desidered result. For start I can copy the current structure:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This XSLT 2.0 stylesheet splits the value by & and then uses a predicate to filter only those that matches() the supplied regex where the parameter names are either pID or download. Using the #separator for xsl:value-of to join the values with &:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="image">
<xsl:copy>
<xsl:text>http://newurl.com/</xsl:text>
<xsl:value-of select="tokenize(., '&')[matches(., '(pID|download).*')]"
separator="&"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Alternatively, this XSLT 2.0 stylesheet uses xsl:analyze-string to extract the text that matches the provided regex:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="image">
<xsl:copy>
<xsl:text>http://newurl.com/</xsl:text>
<xsl:analyze-string select="." regex=".*(pID=.*&download=.*)&.*">
<xsl:matching-substring>
<xsl:value-of select="regex-group(1)"/>
</xsl:matching-substring>
<xsl:non-matching-substring></xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Try this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="image">
<xsl:copy>
<xsl:value-of select="concat('http://newurl.com/pID=', substring-before(substring-after(text(), '&pID='), '&name='))"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I am transforming XML using XSLT and facing issue while namespace removal. If I remove xmlns it works fine.
Issue 1. It doesn't delete namespace from transformed XML
Issue 2. Doesn't implements other template I have in transformation.
My Input XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Catalog xmlns="http://example.com">
<Books>
<book1>Wise1 Otherwise</book1>
<book2>Great Expectations</book2>
</Books>
<library>
<Name> Forsyth </Name>
<city> Cumming </city>
</library>
</Catalog>
Expected Result
<?xml version="1.0" encoding="UTF-8"?>
<Import>
<Books>
<book1>Wise1 Otherwise</book1>
<book2>Great Expectations</book2>
</Books>
<elab>
<Name> Forsyth </Name>
<city> Cumming </city>
</elab>
</Import>
XSL
<?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" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#xmlns">
<xsl:element name="{local-name()}" namespace="http://example.com">
<xsl:apply-templates select="node() | #*"/>
</xsl:element>
</xsl:template>
<xsl:template match="library">
<elab>
<xsl:apply-templates />
</elab>
</xsl:template>
<xsl:template match="Catalog">
<Import>
<xsl:apply-templates />
</Import>
</xsl:template>
</xsl:stylesheet>
I think you are missing the namespace declaration in the XSLT templates to match elements:
This is my try:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://example.com">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="ns:library">
<elab>
<xsl:apply-templates />
</elab>
</xsl:template>
<xsl:template match="ns:Catalog">
<Import>
<xsl:apply-templates />
</Import>
</xsl:template>
</xsl:stylesheet>
I have XML data like this
<ABC version="1.0">
<XYZ>
<ROWSET ROWS="00001">
<ROWDEF>
<COLUMN ID="ACCT_ID" LEN="016" NULL="Y"/>
<COLUMN ID="AGNT_ID" LEN="004" NULL="Y"/>
<COLUMN ID="CUST_EXTR_ID" LEN="024" NULL="Y"/>
<COLUMN ID="PI_ID" LEN="019" NULL="Y"/>
<COLUMN ID="PRIN_ID" LEN="004" NULL="Y"/>
<COLUMN ID="SYS_ID" LEN="004" NULL="Y"/>
</ROWDEF>
<ROW>
<C>6369921501000060</C>
<C>0000</C>
<C>C13093102141063422034238</C>
<C>6369921501000060 </C>
<C>1500</C>
<C>9008</C>
</ROW>
<ROW>
<C>6369921501000061</C>
<C>0001</C>
<C>C13093102141063422034231</C>
<C>6369921501000060 </C>
<C>1501</C>
<C>9001</C>
</ROW>
</ROWSET>
</XYZ>
</ABC>
And I'm trying to convert this into
<?xml version="1.0" encoding="utf-8"?>
<ABC version="1.0">
<XYZ RC="0067">
<ROWSET ROWS="00001">
<ROWDEF>
<ACCT_ID>6369921501000060</ACCT_ID>
<AGNT_ID>0000</AGNT_ID>
<CUST_EXTR_ID>C13093102141063422034238</CUST_EXTR_ID>
<PI_ID>6369921501000060</PI_ID>
<PRIN_ID>1500</PRIN_ID>
<SYS_ID>9008</SYS_ID>
</ROWDEF>
<ROWDEF>
<ACCT_ID>6369921501000061</ACCT_ID>
<AGNT_ID>0001</AGNT_ID>
<CUST_EXTR_ID>C13093102141063422034231</CUST_EXTR_ID>
<PI_ID>6369921501000060</PI_ID>
<PRIN_ID>1501</PRIN_ID>
<SYS_ID>9001</SYS_ID>
</ROWDEF>
</ROWSET>
</XYZ>
</ABC>
I have looked around and tried few things but it is not working.
Can someone help me with this.
Below is my XSLT. Thanks in advance.
<?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"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="COLUMN">
<xsl:element name="{#ID}">
<xsl:copy>
<xsl:apply-templates select="node()"></xsl:apply-templates>
</xsl:copy>
<xsl:apply-templates />
<!--<xsl:call-template name="value"></xsl:call-template>-->
</xsl:element>
</xsl:template>
<!--
<xsl:template match="ROW" name="value">
<xsl:copy>
<xsl:apply-templates select="node()"></xsl:apply-templates>
</xsl:copy>
</xsl:template>
-->
</xsl:stylesheet>
This stylesheet will mostly do what you want, but I cannot fathom how to generate the attribute for <XYZ RC="0067">.
For each ROWSET it comes across it saves the ROWDEF element in a variable, copies all attribute nodes, and then processes each ROW element. The position of each C element in the ROW is caclulated, and the COLUMN in the corresponding position within the stored ROWDEF element is used to fetch an element name from the ID attribute.
<?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"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="ROWSET">
<xsl:variable name="columns" select="ROWDEF/COLUMN"/>
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:for-each select="ROW">
<ROWDEF>
<xsl:for-each select="C">
<xsl:variable name="pos" select="position()"/>
<xsl:element name="{$columns[$pos]/#ID}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</ROWDEF>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
output
<?xml version="1.0" encoding="utf-8"?>
<ABC version="1.0">
<XYZ>
<ROWSET ROWS="00001">
<ROWDEF>
<ACCT_ID>6369921501000060</ACCT_ID>
<AGNT_ID>0000</AGNT_ID>
<CUST_EXTR_ID>C13093102141063422034238</CUST_EXTR_ID>
<PI_ID>6369921501000060 </PI_ID>
<PRIN_ID>1500</PRIN_ID>
<SYS_ID>9008</SYS_ID>
</ROWDEF>
<ROWDEF>
<ACCT_ID>6369921501000061</ACCT_ID>
<AGNT_ID>0001</AGNT_ID>
<CUST_EXTR_ID>C13093102141063422034231</CUST_EXTR_ID>
<PI_ID>6369921501000060 </PI_ID>
<PRIN_ID>1501</PRIN_ID>
<SYS_ID>9001</SYS_ID>
</ROWDEF>
</ROWSET>
</XYZ>
</ABC>
I have a style-sheet that I am using to remove certain elements based on the value of an other element. However, it is not working ...
Sample Input XML
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
<Text>Testing</Text>
<Status>Ok</Status>
</Model>
If Operation value is 'ABC' then remove Text and Status nodes from XML.
And gives the following output.
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
</Model>
Here is my style sheet that I am using but it is removing Text and Status nodes from all XMLs even when operation is not 'ABC'.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="ID" select="//Operation"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Text | Status">
<xsl:if test ="$ID ='ABC'">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Thanks in Advance
How would I do the same when namespace is present like
<ns0:next type="Sale" xmlns:ns0="http://Test.Schemas.Inside_Sales">
Here is a complete XSLT transformation -- short and simple (no variables, no xsl:if, xsl:choose, xsl:when, xsl:otherwise):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[Operation='ABC']/Text | *[Operation='ABC']/Status"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
<Text>Testing</Text>
<Status>Ok</Status>
</Model>
the wanted, correct result is produced:
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
</Model>
Change your xsl:if as follows:
<xsl:if test="../Operation!='ABC'">
and you can get rid of xsl:variable.
A better pattern in XSLT than using <xsl:if> is to add new templates with match conditions:
<xsl:template match="(Text | Status)[../Operation != 'ABC']"/>
I found this works:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Model">
<xsl:choose>
<xsl:when test="Operation[text()!='ABC']">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="Year"/>
<xsl:apply-templates select="Operation"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>