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

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.

Related

Applying redactions in the form of string substitutions to HTML documents using XSLT

I have a large number of HTML (and possibly other xml) documents that I need to redact.
The redactions are typically of the form "John Doe" -> "[Person A]". The text to be redacted may be in headers or paragraphs, but will almost always be in paragraphs.
Simple string substitutions really. Not very complicated things.
However, I do want to preserve document structure, and I would prefer to not reinvent any wheels. String substitution in the document text may do the job, but also may break document structure, so it will be a last option.
Right now I have stared at XSLT for an hour and tried to force "str:replace" to do my bidding. I will spare you from viewing me feeble attempts that didn't work, but I will ask this: Is there a simple and know way to apply my redactions using XSLT, and could you post it here?
Thank you in advance.
Update: at the request of Martin Honnen I'm adding my input files, as well as the command I used to get the latest error message. From this it will be apparent that I'm a complete n00b when it comes to XSLT :-)
.html file:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>TodaysDate</title>
<meta name="created" content="2020-11-04T30:45:00"/>
</head>
<body>
<ol start="2">
<li><p> John Doe on 9. fux 2057 together with Henry
Fluebottom formed the company Doe &; Fluebottom Widgets
Inc. </p>
</ol>
</body>
</html>
The XSLT transformation file:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="p">
<xsl:copy>
<xsl:attribute name="matchesPattern">
<xsl:copy-of select='str:replace("John Doe", ".*", "[Person A]")'/>
</xsl:attribute>
<xsl:copy-of select='str:replace("Henry Fluebottom", ".*", "[Person B]")'/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The command and the output:
$ xsltproc -html transform.xsl example.html
xmlXPathCompOpEval: function replace bound to undefined prefix str
xmlXPathCompiledEval: 2 objects left on the stack.
<?xml version="1.0"?>
TodaysDate
<p matchesPattern=""/>
$
xsltproc is based on libxslt and that way supports various EXSLT functions like str:replace, to use it you will need to declare the namespace
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
exclude-result-prefixes="str"
version="1.0">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p//text()">
<xsl:value-of select="str:replace(., 'John Doe', '[Person A]')"/>
</xsl:template>
</xsl:stylesheet>
There is no simple way in XSLT 1.0 to perform multiple replacements on the same string. You need to use a recursive named template, performing one replacement operation at a time, then moving to the next instance of the current find string or - when no next instance exists - to the next find/replace pair.
Consider the following example:
Input
<html>
<head>
<title>John Doe and Henry Fluebottom</title>
</head>
<body>
<p>John Doe is a person. John Doe on 9. fux 2057 together with Henry Fluebottom formed the company Doe & Fluebottom Widgets Inc. Henry Fluebottom is also a person.</p>
</body>
</html>
XSLT 1.0 (+ EXSLT node-set() function)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="dictionary">
<entry find="John Doe" replace="[Person A]"/>
<entry find="Henry Fluebottom" replace="[Person B]"/>
</xsl:variable>
<xsl:template match="text()">
<xsl:call-template name="multi-replace">
<xsl:with-param name="string" select="normalize-space(.)"/>
<xsl:with-param name="entries" select="exsl:node-set($dictionary)/entry"/>"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="multi-replace">
<xsl:param name="string"/>
<xsl:param name="entries"/>
<xsl:choose>
<xsl:when test="$entries">
<xsl:call-template name="multi-replace">
<xsl:with-param name="string">
<xsl:call-template name="replace">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="search-string" select="$entries[1]/#find"/>
<xsl:with-param name="replace-string" select="$entries[1]/#replace"/>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="entries" select="$entries[position() > 1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="replace">
<xsl:param name="string"/>
<xsl:param name="search-string"/>
<xsl:param name="replace-string"/>
<xsl:choose>
<xsl:when test="contains($string, $search-string)">
<xsl:value-of select="substring-before($string, $search-string)"/>
<xsl:value-of select="$replace-string"/>
<xsl:call-template name="replace">
<xsl:with-param name="string" select="substring-after($string, $search-string)"/>
<xsl:with-param name="search-string" select="$search-string"/>
<xsl:with-param name="replace-string" select="$replace-string"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Result
<html>
<head>
<title>[Person A] and [Person B]</title>
</head>
<body>
<p>[Person A] is a person. [Person A] on 9. fux 2057 together with [Person B] formed the company Doe & Fluebottom Widgets Inc. [Person B] is also a person.</p>
</body>
</html>
As you can see, this replaces all instances of the search strings anywhere in the input document (except for attributes), while preserving the document's structure.
Note that the input in your example does not actually contain the "Henry Fluebottom" search string. You might want to get around that by calling the first template with:
<xsl:with-param name="string" select="normalize-space(.)"/>
instead of:
<xsl:with-param name="string" select="."/>
The first problem is to find an XSLT processor that actually supports string replacement. The replace() function is standard in XSLT 2.0+, but does not exist in XSLT 1.0. Some XSLT 1.0 processors support an extension function str:replace() in a different namespace, but at the very least, you need to add the namespace declaration xmlns:str="http://exslt.org/strings" to your stylesheet in order to locate the function. I don't know if that will work (I don't know if there is any way of using this function with xsltproc); my advice would be to use an XSLT 2.0+ processor instead.
The next problem is the way you are invoking the function. Typically, a correct invocation would be
replace(., "John Doe", "[Person A]")
though you will have to jump through a few more hoops to make multiple replacements on the same string.
I've no idea what you are trying to achieve with the <xsl:attribute name="matchesPattern"> instruction.

Select Parent Node without Child node. XSLT

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>

How can I transform an incoming SOAP response into a JSON document using WSO2 ESB

I would like to convert json to xml using ESB4.10. And need to know and find out how does JBoss ESB support JSON http request? If this has to be done using the HTTP Request do share some thread that I can use to implement this.
Within your proxy or REST API element, if you are willing to convert the given response (SOAP) back to JSON just simply include the following properties in your out sequence:
<xslt key="ns-remove"/>
<property name="messageType" value="application/json" scope="axis2"/>
Include this section in registry, some generally we need to remove any namespaces available.
<localEntry key="ns-remove">
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*"/>
<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="/">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="comment() | processing-instruction() | text()">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
</localEntry>

XLST + XML -> Word <field name="foo">Value</foo>

I have a XML produce with MySQL Query Browser.
I'm trying to apply a XSLT to output the result into Word tables. One table for each record.
Here's a sample of my XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ROOT SYSTEM "Nessus.dtd">
<ROOT>
<row>
<field name="Niveau">Critique</field>
<field name="Name">Apache 2.2 < 2.2.15 Multiple Vulnerabilities</field>
</row>
<row>
<field name="Niveau">Critique</field>
<field name="VulnName">Microsoft Windows 2000 Unsupported Installation Detection</field>
</row>
<row>
<field name="Niveau">Haute</field>
<field name="VulnName">CGI Generic SQL Injection</field>
</row>
</ROOT>
For the XLST I've already found out that I need to do a for-each select
<?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:template match="/">
<xsl:for-each select="ROOT/row">
Niveau : <xsl:value-of select="????"/>
Name : <xsl:value-of select="????"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When I do this loop I see the same number of empty table as there is <row></row> in my file.
But I haven't found the way to make the right "value-of select=". I've try the following without luck.
<xsl:value-of select="#name"/>
<xsl:value-of select="name"/>
<xsl:value-of select="#row/name"/>
<xsl:value-of select="row/#name"/>
<xsl:value-of select="#ROOT/row/name"/>
And a few other that I can't remember. Any idea what I need to craft the request to get the value in my resulting file?
I've just tried with :
<?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:template match="/">
<xsl:for-each select="ROOT/row">
Niveau : <xsl:value-of select="field/#Niveau"/>
Name : <xsl:value-of select="field/#name"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
And it output this :
NIVEAU :
NAME : name
NIVEAU :
NAME : Niveau
NIVEAU :
NAME : Niveau
I would like this output :
NIVEAU : Critique
NAME : Apache 2.2 < 2.2.15 Multiple Vulnerabilities
NIVEAU : Critique
NAME : Microsoft Windows 2000 Unsupported Installation Detection
NIVEAU : Haute
NAME : CGI Generic SQL Injection
Any help would be appreciated.
Thank you.
UPDATE
Now with this XSLT
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="row">
<xsl:text>NIVEAU : </xsl:text>
<xsl:value-of select="field[#name = 'Niveau']"/>
<xsl:text>
</xsl:text>
<xsl:text>NAME : </xsl:text>
<xsl:value-of select="field[#name = 'Name']"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
I get this output :
<?xml version="1.0"?>
NIVEAU :
NAME : Apache 2.2 < 2.2.15 Multiple Vulnerabilities
NIVEAU : Critique
NAME : Microsoft Windows 2000 Unsupported Installation Detection
NIVEAU : Haute
NAME : CGI Generic SQL Injection
As you can see the first field is empty. I could honestly live with that and fill it manually but if you see why this is happenning I'd be very happy :)
UPDATE
Using <xsl:value-of select="field[#name = 'foo']"/> gave me the value I wanted. I kept the for-each as it was easier to use (for me) inside a MS Word template.
for-each is generally code smell in XSLT. You most likely want a template, not a for-each loop:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="field">
<xsl:value-of select="#name"/> : <xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
This template will produce the following output:
<root>
Niveau : Critique
name : Apache 2.2 < 2.2.15 Multiple Vulnerabilities
Niveau : Critique
name : Microsoft Windows 2000 Unsupported Installation Detection
Niveau : Haute
name : CGI Generic SQL Injection
</root>
XSLT was designed for this pattern of use--many xsl:templates matching a small part of the source and applying other templates recursively. The most important and common template in this pattern is the identity template, which copies output. This is a good tutorial on XSLT coding patterns that you should read.
Update
Below is a complete solution that will produce text output (since that is what you seem to want, not XML output). I'm not sure xsl is the best language for this, but it works....
Notice that the only place we use for-each is for sorting to determine the length of the longest name. We use templates and pattern-matching selects to make all other loops implicit.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="newline"><xsl:text>
</xsl:text></xsl:variable>
<!-- determine the maximum length of the "name" field so we can pad with spaces for all shorter items -->
<xsl:variable name="max_name_len">
<xsl:for-each select="ROOT/row/field/#name">
<xsl:sort select="string-length(.)" data-type="number" order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="string-length(.)"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- for each row, apply templates then add a blank line -->
<xsl:template match="row">
<xsl:apply-templates/>
<xsl:value-of select="$newline"/>
</xsl:template>
<!-- for each field, apply template to name and add value, followed by a newline -->
<xsl:template match="field">
<xsl:apply-templates select="#name"/> : <xsl:value-of select="concat(., $newline)"/>
</xsl:template>
<!-- for each name, uppercase and pad with spaces to the right -->
<xsl:template match="field/#name">
<xsl:call-template name="padright">
<xsl:with-param name="text">
<xsl:call-template name="toupper">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="len" select="$max_name_len"/>
</xsl:call-template>
</xsl:template>
<!-- Utility function: uppercase a string -->
<xsl:template name="toupper">
<xsl:param name="text"/>
<xsl:value-of select="translate($text, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
</xsl:template>
<!-- Utility function: pad a string to desired len with spaces on the right -->
<!-- uses a recursive solution -->
<xsl:template name="padright">
<xsl:param name="text"/>
<xsl:param name="len"/>
<xsl:choose>
<xsl:when test="string-length($text) < $len">
<xsl:call-template name="padright">
<xsl:with-param name="text" select="concat($text, ' ')"/>
<xsl:with-param name="len" select="$len"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This stylesheet produces the following output:
NIVEAU : Critique
NAME : Apache 2.2 < 2.2.15 Multiple Vulnerabilities
NIVEAU : Critique
NAME : Microsoft Windows 2000 Unsupported Installation Detection
NIVEAU : Haute
NAME : CGI Generic SQL Injection
When you do <xsl:for-each select="ROOT/row">, the current context inside the loop is the rowelement. So in order to access the field name, you need to write, for example, <xsl:value-of select="field/#name"/>.
Since your XML contains several fields, you will still have to extend your XSLT file somewhat to iterate the fields, or (as Francis Avila suggested) write a template. Both methods are ok, though. The technical terms for the two approaches are "pull" and "push", respectively.
And of course, since you ultimately want to generate a word table, you will have to generate w:tr and w:tc elements, etc.
This stylesheet will give you exactly your desired output
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="row">
<xsl:text>NIVEAU : </xsl:text>
<xsl:value-of select="field[#name eq 'Niveau']"/>
<xsl:text>
</xsl:text>
<xsl:text>NAME : </xsl:text>
<xsl:value-of select="field[#name eq 'VulnName']"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
This is very specific to your input xml and output text, for example any <field> children of <row> other than 'Niveau' or 'VulnName' will be dropped from your report. A more generic solution could look like this:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="text"/>
<xsl:template match="field">
<xsl:value-of select="concat(upper-case(#name),': ',.)"/>
</xsl:template>
</xsl:stylesheet>
This solution though doesn't exactly match the whitespace formatting in your desired output, but it does capture all possible fields.

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.