Select Parent Node without Child node. XSLT - html

I would like to select Parent node without Child node.
Example:
<Shop>
<Product>
<ProductId>1</ProductId>
<Description>ProductList</Description>
<Milk>
<MilkId></MilkId>
</Milk>
</Product>
</Shop>
Desired Output:
<Shop>
<Product>
<ProductId>1</ProductId>
<Description>ProductList</Description>
</Product>
</Shop>
I tried below XSLT but it failed to return correct result:
<xsl:copy-of select="//Product/not[Milk]"/>
Thank you for any help.
Update:
XSLT:
<xsl:copy-of select="Product/*[not(self::Milk)]" />
Returns:
<ProductId>1</ProductId>
I need below structure to be returned:
<Shop>
<Product>
<ProductId>1</ProductId>
<Description>ProductList</Description>
</Product>
</Shop>

You can use a variant of the Identity template:
<!-- Identity template variant -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()[local-name()!='Milk']|#*" />
</xsl:copy>
</xsl:template>
Or, as a more XSLT-2.0 way suggested in the comments
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node() except Milk|#*" />
</xsl:copy>
</xsl:template>
It copies all nodes except for the ones named Milk and its children.
If you want to apply this only to Product nodes, you also have to use the Identity template and change the matching rule to
<xsl:template match="Product">...
A solution using only xsl:copy-of could be copying the Product element and then copy all of its children (except for the Milk ones) with
<xsl:copy-of select="Product/*[not(self::Milk)]" />
Or, in a whole XSLT-2.0 template
<xsl:template match="//Product">
<xsl:copy>
<xsl:copy-of select="* except Milk" />
</xsl:copy>
</xsl:template>
A whole XSLT-1.0 stylesheet could look like
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="Product">
<xsl:copy>
<xsl:copy-of select="*[not(self::Milk)]" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Its output is:
<?xml version="1.0"?>
<Shop>
<Product>
<ProductId>1</ProductId>
<Description>ProductList</Description>
</Product>
</Shop>

Related

XSLT Apply potentially multiple templates to a single node depending on conditions

Here is my need:
I have an HTML text composed from multiple nested SPAN.
Some of the spans include inline css. In that inline CSS, I need to transform SOME OF the style attributes into "classical" HTML, like .
Some spans have one or many of the attributes to be replaced by HTML elements.
Here is an example input :
<HTML>
<SPAN>
Some sentance including another
<SPAN STYLE="font-weight: bold;" >
included bold block
</SPAN>
with a tail in it and line breaks<BR/>
</SPAN>
<SPAN STYLE= "font-family: 'Helvetica';font-weight: bold;" >
Another span with 1 attribute not to be taken into account and 1 to be
</SPAN>
<SPAN STYLE= "font-family: 'Helvetica';font-weight: bold;text-decoration:underline;" >
Another span with two attributes to be taken into account
</SPAN>
</HTML>
What I would like as a result is :
<HTML>
<p>Some sentance including another
<p><b>included bold block </b></p>
with a tail in it and line breaks<BR/></p>
<p><b>Another span with 1 attribute not to be taken into account and 1 to be</b></p>
<p><b><u>Another span with two attributes to be taken into account</u></b></p>
</HTML>
I thought the best way to do it would be to use the Identity Transformation and to have templates to match the attributes with conditions like :
SPAN[contains(#STYLE, 'font-weight: bold;')
and :
SPAN[contains(#STYLE, 'text-decoration:underline;')]
Here is what I tried...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output 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="SPAN[contains(#STYLE, 'text-decoration:underline;')]">
<xsl:element name="u">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="SPAN[contains(#STYLE, 'font-weight: bold;')]">
<xsl:element name="b">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="SPAN">
<!-- replacing SPAN into <p>elements -->
<xsl:element name="p">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="SPAN/#STYLE"/>
<!-- suppression of the old Style attribute-->
</xsl:stylesheet>
The problem is, when it runs, it only matches either one of the the or the
I think I mis-understand how to use the xsl:copy in the templates to ask the template to evaluate for a SPAN and to re-evaluate the other templates for the same span, but I did not succeed to have it work with or without it...
I thank you in advance for your thoughts about it.
Best regards.
Here how you can do it with modes in XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output 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="SPAN[contains(#STYLE, 'text-decoration:underline;')]" mode="u">
<u>
<xsl:apply-templates/>
</u>
</xsl:template>
<xsl:template match="SPAN" mode="u">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="SPAN[contains(#STYLE, 'font-weight: bold;')]" mode="b">
<b>
<xsl:apply-templates select="." mode="u" />
</b>
</xsl:template>
<xsl:template match="SPAN" mode="b">
<xsl:apply-templates select="." mode="u" />
</xsl:template>
<xsl:template match="SPAN">
<p>
<xsl:apply-templates select="." mode="b" />
</p>
</xsl:template>
</xsl:stylesheet>
Note how a template matching SPAN[contains(...)] has a higher priority than one just matching SPAN.
So, effectively you have a change of templates. For a SPAN tag, a p node is output, then the b template is called to check for bold. The b templates then calls the u template to check for underline, and this template then processes the children. You can add more templates in a similar fashion (so b calls u, then u calls i, and the i processes the children).
EDIT: If you wanted to avoid writing two templates for each, you could combine each pair of templates into one, like so
<xsl:template match="SPAN" mode="b">
<xsl:choose>
<xsl:when test="contains(#STYLE, 'font-weight: bold;')">
<b>
<xsl:apply-templates select="." mode="u" />
</b>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="." mode="u" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And if you could use XSLT 2.0, you could use priorities, and next-match instead, which reduces the number of templates
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output 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="SPAN">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="SPAN[contains(#STYLE, 'text-decoration:underline;')]" priority="8">
<u>
<xsl:next-match />
</u>
</xsl:template>
<xsl:template match="SPAN[contains(#STYLE, 'font-weight: bold;')]" priority="9">
<b>
<xsl:next-match />
</b>
</xsl:template>
<xsl:template match="SPAN" priority="10">
<p>
<xsl:next-match />
</p>
</xsl:template>
</xsl:stylesheet>

How to change the format of XML schema I get from SQL server

I have a query:
SELECT top 0 * FROM sometable FOR XML AUTO, ELEMENTS, XMLSCHEMA ('MyURI')
This query returns a schema:
<xsd:element name="ClientName">
<xsd:simpleType>
<xsd:restriction base="sqltypes:nvarchar" sqltypes:localeId="1033" sqltypes:sqlCompareOptions="IgnoreCase IgnoreKanaType IgnoreWidth" sqltypes:sqlSortId="52">
<xsd:maxLength value="50" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
but I want something more like this:
<xsd:element name="ClientName">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:maxLength value="50" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
How can I achieve this?
You can use a XSL transform to change the SQL Server types back to the desired XSD types.
How apply the transform depends on your application. If you are creating static schemata for your tables, then you can use something like Visual Studio or msxsl. If this is a routine request from the server, then Applying an XSL Transformation (SQLXML Managed Classes) may be a better fit.
A style sheet you can build on with additional types is:
<?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:sqltypes="http://schemas.microsoft.com/sqlserver/2004/sqltypes"
xmlns="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" indent="yes"/>
<xsl:param name="Strip">false</xsl:param>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#base">
<xsl:attribute name="{name()}">
<xsl:choose>
<xsl:when test=".='sqltypes:nvarchar'">xsd:string</xsl:when>
<!-- Add additional tests here -->
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:template>
<xsl:template match="#sqltypes:*">
<xsl:if test="$Strip=true">
<xsl:comment>
<xsl:text>Stripped (</xsl:text>
<xsl:value-of select="concat(name(), '="', ., '"')"/>
<xsl:text>)</xsl:text>
</xsl:comment>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Change the Strip parameter at the beginning to false if you need to see the attributes that are stripped in the transformation process.

how to interpret HTML in XSL

I have the following xml
<results>
<first-name>Carl<first-name>
<data><b> This is carl's data </b></data>
</results>
How do I include the bold tags which is present in the <data> tag to be a part of the output but rendered as an HTML
When I say <xsl:value-of select="results/data"/> The output is
<b> This is carl's data </b>
I want to achieve "This is carl's data" as the output in bold.
Well <xsl:copy-of select="results/data/node()"/> is a start but if the requirement is part of a larger problem then you are better off writing a template for data elements which uses apply-templates to push the child nodes through some template(s) for copying HTML elements through to the output.
I am sure someone will let me know if I am being naive:
<?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="html" indent="yes"/>
<xsl:template match="/results">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="first-name">
<xsl:value-of select="." />
<xsl:text>: </xsl:text>
</xsl:template>
<xsl:template match="data">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="b">
<b>
<xsl:value-of select="." />
</b>
</xsl:template>
</xsl:stylesheet>

Preserve certain html tags during XSLT

I have looked up solutions on stackflow, but none of them seem to work for me. Here is my question. Lets say I have the following text :
Source:
<greatgrandparent>
<grandparent>
<parent>
<sibling>
Hey, im the sibling .
</sibling>
<description>
$300$ <br/> $250 <br/> $200! <br/> <p> Yes, that is right! <br/> You can own a ps3 for only $200 </p>
</description>
</parent>
<parent>
... (SAME FORMAT)
</parent>
... (Several more parents)
</grandparent>
</greatgrandparent>
Output:
<newprice>
$300$ <br/> $250 <br/> $200! <br/> Yes, that is right! <br/> You can own a ps3 for only $200
</newprice>
I can't seem to find a way to do that.
Current XSL:
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="greatgrandparents">
<xsl:apply-templates />
</xsl:template>
<xsl:template match = "grandparent">
<xsl:for-each select = "parent" >
<newprice>
<xsl:apply-templates>
</newprice>
</xsl:for-each>
</xsl:template>
<xsl:template match="description">
<xsl:element name="newprice">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="p">
<xsl:apply-templates/>
</xsl:template>
Use templates to define behavior on specific elements
<!-- after standard identity template -->
<xsl:template match="description">
<xsl:element name="newprice">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="p">
<xsl:apply-templates/>
</xsl:template>
The first template says to swap description with newprice. The second one says to ignore the p element.
If you're unfamiliar with the identity template, take a look here for a few examples.
EDIT: Given the new example, we can see that you want to only extract the description element and its contents. Notice that the template action starts with the match="/" template. We can use this control where our stylesheet starts and thus skip much of the riffraff we want to filter out.
change the <xsl:template match="/"> to something more like:
<xsl:template match="/">
<xsl:apply-templates select="//description"/>
<!-- use a more specific XPath if you can -->
</xsl:template>
So altogether our solution looks like this:
<xsl:stylesheet
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
exclude-result-prefixes="xs">
<xsl:template match="/">
<xsl:apply-templates select="//description" />
</xsl:template>
<!-- this is the identity template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="description">
<xsl:element name="newprice">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="p">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Shouldn't the contents of be inside a CDATA element? And then probably disable output encoding on xsl:value-of..
You should look into xsl:copy-of.
You would probably wind up with somthing like:
<xsl:template match="description">
<xsl:copy-of select="."/>
</xsl:template>
Probably the shortest solution is this one:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="description">
<newprice>
<xsl:copy-of select="node()"/>
</newprice>
</xsl:template>
<xsl:template match="text()[not(ancestor::description)]"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document, the wanted result is produced:
<newprice>
$300$ <br /> $250 <br /> $200! <br /> <p> Yes, that is right! <br /> You can own a ps3 for only $200 </p>
</newprice>
Do note:
The use of <xsl:copy-of select="node()"/> to copy all the subtree rooted in description, without the root itself.
How we override (with a specific, empty template) the XSLT built-in template, preventing any text nodes that are not descendents of a <description> element, to be output.

XLSX- how to get rid of the default namespace prefix x:?

I'm generating XLSX spreadsheet using OOXML SDK, and I need to get rid of x: namespace prefix. How can I achieve this?
using (SpreadsheetDocument doc = SpreadsheetDocument.Open("template.xlsx", true))
{
//Save the shared string table part
if (doc.WorkbookPart.GetPartsOfType().Count() > 0)
{
SharedStringTablePart shareStringPart = doc.WorkbookPart.GetPartsOfType().First();
shareStringPart.SharedStringTable.Save();
}
//Save the workbook
doc.WorkbookPart.Workbook.Save();
}
Here, the original XLSX file is coming from Excel 2007 and doesn't have the prefix, however, after the save operation the prefix appears. How can I avoid that?
Here is a modified version of the stylesheet linked by divo that strips only a single namespace and copies the rest verbatim:
<xsl:stylesheet version="1.0" xmlns:x="namespace-to-strip"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" encoding="UTF-8"/>
<xsl:template match="/|comment()|processing-instruction()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="x:*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#x:*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Unless I'm much mistaken the original file is namespaced as well - only in the form of a default namespace. What's wrong with the namespace in the first place?