I have the following XML:
<DEVICEMESSAGES>
<VERSION xml="1" checksum="" revision="0" envision="33050000" device="" />
<HEADER id1="0001" id2="0001" content="Nasher[<messageid>]: <!payload>" />
<MESSAGE level="7" parse="1" parsedefvalue="1" tableid="15" id1="24682" id2="24682" eventcategory="1003010000" content="Access to <webpage> was blocked due to its category (<info> by <hostname>)" />
</DEVICEMESSAGES>
I am using the following XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="DEVICEMESSAGES/HEADERS">
<xsl:value-of select="#id2"/>,<xsl:text/>
<xsl:value-of select="#content"/>,<xsl:text/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
I get the following output:
0001 , Nasher[<messageid>]: <!payload>
whereas I need the column headings, too:
id2, content
0001 , Nasher[<messageid>]: <!payload>
If DEVICEMESSAGES is the document element and you have repeating MESSAGE elements, then this should work:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="DEVICEMESSAGES">
<xsl:text>id2,content,
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="DEVICEMESSAGES/HEADER">
<xsl:value-of select="#id2"/>,<xsl:text/>
<xsl:value-of select="#content"/>,<xsl:text/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
If you have a different document element, then adjust the template to match on that.
For instance, if the document element is doc and you had 1 to N number of DEVICEMESSAGES elements:
<doc>
<DEVICEMESSAGES>
<VERSION xml="1" checksum="" revision="0" envision="33050000" device="" />
<HEADER id1="0001" id2="0001" content="Nasher[<messageid>]: <!payload>" />
<MESSAGE level="7" parse="1" parsedefvalue="1" tableid="15" id1="24682" id2="24682" eventcategory="1003010000" content="Access to <webpage> was blocked due to its category (<info> by <hostname>)" />
</DEVICEMESSAGES>
<DEVICEMESSAGES>
<VERSION xml="1" checksum="" revision="0" envision="33050000" device="" />
<HEADER id1="0002" id2="0002" content="Nasher[<messageid>]: <!payload>" />
<MESSAGE level="7" parse="1" parsedefvalue="1" tableid="15" id1="24682" id2="24682" eventcategory="1003010000" content="Access to <webpage> was blocked due to its category (<info> by <hostname>)" />
</DEVICEMESSAGES>
</doc>
then you could use this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="doc">
<xsl:text>id2,content,
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="DEVICEMESSAGES/HEADER">
<xsl:value-of select="#id2"/>,<xsl:text/>
<xsl:value-of select="#content"/>,<xsl:text/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
EDIT: Another alternative that uses the template match of the root node and does not need to know what the document element is:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:text>id2,content,
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="DEVICEMESSAGES/HEADER">
<xsl:value-of select="#id2"/>,<xsl:text/>
<xsl:value-of select="#content"/>,<xsl:text/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Related
Below is what my XML looks like. I have also copied XSLT and output below. My problem is that, the output is always repeating the same first row. If I add more rows to input XML file then first row would repeat in output file for that many number of rows added. What could be the reason?
XML:
<Loans>
<Loan>
<loan_number>123</loan_number>
<loan_aqn_date>08-01-2016</loan_number>
</Loan>
<Loan>
<loan_number>456</loan_number>
<loan_aqn_date>10-01-2016</loan_number>
</Loan>
<Loan>
<loan_number>789</loan_number>
<loan_aqn_date>12-01-2016</loan_number>
</Loan>
</Loans>
Output:
loan_number|loan_aqn_date|
123|08-01-2016|
123|08-01-2016|
123|08-01-2016|
XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="/">
<xsl:text>loan_number|loan_aqn_date|</xsl:text>
<xsl:for-each select="/Loans/Loan">
<xsl:value-of select="concat(/Loans/Loan/loan_number,'|')" />
<xsl:value-of select="concat(/Loans/Loan/loan_aqn_date,'|')" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You are using an absolute path for "select" inside loop.
Try this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="/">
<xsl:text>loan_number|loan_aqn_date|</xsl:text>
<xsl:for-each select="/Loans/Loan">
<xsl:value-of select="concat(loan_number,'|')" />
<xsl:value-of select="concat(loan_aqn_date,'|')" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Replacing the <for-each> with a template could make your approach more general.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:strip-space elements="Loans" /> <!-- Removes surrounding spaces -->
<xsl:template match="/Loans">
<xsl:text>loan_number|loan_aqn_date|
</xsl:text>
<xsl:apply-templates /> <!-- Processes 'Loan' nodes -->
</xsl:template>
<xsl:template match="Loan">
<xsl:value-of select="concat(loan_number, '|', loan_aqn_date,'|')" />
<xsl:text>
</xsl:text> <!-- Adds newlines -->
</xsl:template>
</xsl:stylesheet>
Output:
loan_number|loan_aqn_date|
123|08-01-2016|
456|10-01-2016|
789|12-01-2016|
Input file as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!-- lower UPPER case -->
<document>
<rubbish> rubbish </rubbish>
<span class='lower'>
lower
<span class='upper'> upper </span>
case
</span>
</document>
Wanted output:
lower UPPER case
I know how to get the text included in the outer span with value-of, but this also
includes the string "upper" unchanged which is not what I want. I do not know how
to manipulate the text in the inner span and insert it in the middle of
the other text.
Failed attempt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text" indent="no"/>
<xsl:template match="/">
<xsl:for-each select="//span[#class = 'lower']">
<xsl:if test="span/#class = 'upper'">
<xsl:text>do something</xsl:text> <!--TO DO -->
</xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You need to take a recursive approach here, for example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="text()[parent::span]">
<xsl:choose>
<xsl:when test="../#class='upper'">
<xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
To understand how this works, read up on built-in template rules: http://www.w3.org/TR/xslt/#built-in-rule
The following approach does away with the <choose> and completely pushes the problem down to the match expression:
<?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="text()"/>
<xsl:template match="text()[parent::span[#class = 'upper']]">
<xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
</xsl:template>
<xsl:template match="text()[parent::span[#class = 'lower']]">
<xsl:value-of select="translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
</xsl:template>
</xsl:stylesheet>
This is my xml file:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="try.xsl"?>
<oneday>
<team1 id="1" team="India">
<team2 id="2" team="gujarat">
<team3 id="3" team="guj11"></team3>
</team2>
</team1>
</oneday>
This is my xsl file:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:output method="text" />
<xsl:template match="*">
<xsl:value-of select="local-name()"/>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()[normalize-space(.)]">
<xsl:value-of select="concat(':', .)"/>
</xsl:template>
</xsl:stylesheet>
This is my output:
oneday
team1
team2
team3
But I want to output something like this:
oneday
team1:India
team2:Gujarat
team3:Guj11
How's this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="*">
<xsl:apply-templates select="ancestor::*" mode="spacing" />
<xsl:value-of select="local-name()"/>
<xsl:if test="#team">
<xsl:value-of select="concat(':', #team)"/>
</xsl:if>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="*" />
</xsl:template>
<xsl:template match="*" mode="spacing">
<xsl:text> </xsl:text>
</xsl:template>
</xsl:stylesheet>
When run on your sample XML, this produces:
oneday
team1:India
team2:gujarat
team3:guj11
When run on a slightly more complex XML:
<oneday>
<team1 id="1" team="India">
<team2 id="2" team="gujarat">
<team3 id="3" team="guj11"></team3>
</team2>
<team6 team="Mumbai" />
</team1>
<team4 team="France">
<team5 team="Paris" />
</team4>
</oneday>
It produces:
oneday
team1:India
team2:gujarat
team3:guj11
team6:Mumbai
team4:France
team5:Paris
XML coding is given below
<math>
<mn>
<mphantom>12</mphantom>
</mn>
</math>
Output required:
<?xml version='1.0' encoding='UTF-8'?>
<math>|phantom1||phantom2|</math>
XSLT tried:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://www.w3.org/1998/Math/MathML"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cl="http://xml.cengage-learning.com/cendoc-core"
xmlns:mml="http://www.w3.org/1998/Math/MathML"
xmlns:xlink="http://www.w3.org/1999/xlink">
<xsl:output method="xml" encoding="UTF-8" indent="no" />
<xsl:strip-space elements="*" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="mn">
<xsl:variable name="pText" select="." />
<xsl:variable name="numst" select="2" />
<xsl:choose>
<xsl:when test="not(string-length($pText))>0">
<xsl:value-of select="$pText" />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="ConcatStr" select="substring($pText,$numst)" />
<xsl:variable name="FinalStr" select="substring-before($pText,$ConcatStr)" />
<xsl:text disable-output-escaping="yes">|phantom</xsl:text>
<xsl:value-of select="$FinalStr" />
<xsl:text disable-output-escaping="yes">|</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="mphantom">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
I used the above XSLT to transform but I am getting only the first number. But I need to use it as recursive. I am using XSLT 1.0. Kindly suggest
Here is a simpler, non-recursive solution, using the Piez method:
<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="mphantom">
<math>
<xsl:variable name="vCur" select="."/>
<xsl:for-each select=
"(document('')//node()|document('')//#*|document('')//namespace::*)
[not(position() > string-length($vCur))]
">
<xsl:value-of select="concat('|', substring($vCur, position(), 1),'|')"/>
</xsl:for-each>
</math>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<math>
<mn>
<mphantom>12</mphantom>
</mn>
</math>
the wanted, correct result is produced:
<math>|1||2|</math>
II. For completeness, here is an XSLT 2.0 solution:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="mphantom">
<math>
<xsl:value-of separator="" select=
"for $i in 1 to string-length()
return
concat('|', substring(., $i, 1), '|')
"/>
</math>
</xsl:template>
</xsl:stylesheet>
Try this..
<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="mn[mphantom]" name="phant">
<xsl:param name="chars" select="mphantom" />
<xsl:value-of select="concat('|phantom',substring($chars,1,1),'|')" />
<xsl:if test="substring($chars,2)">
<xsl:call-template name="phant">
<xsl:with-param name="chars" select="substring($chars,2)" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I'm trying to convert an XML file into a flat, pipe-delimited file with XSLT (for bulk-loading into Postgres). I would like the last column in my output to be the actual XML of the node (for additional post-processing and debugging). For example:
<Library>
<Book id="123">
<Title>Python Does Everythig</Title>
<Author>Smith</Author>
</Book>
<Book id="456">
<Title>Postgres is Neat</Title>
<Author>Wesson</Author>
</Book>
</Library>
Should generate
Python Does Everything|Smith|<Book id="123"><Title>Python Does Everythig</Title>Author>Smith</Author></Book>
Postgres is Neat|Wesson|<Book id="456"><Title>Postgres is Neat</Title><Author>Wesson</Author></Book>
My current XSL is
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:output method="text" omit-xml-declaration="yes" indent="no" />
<xsl:template match="//Book">
<xsl:value-of select="Title" />
<xsl:text>|</xsl:text>
<xsl:value-of select="Author" />
<!-- put in the newline -->
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
I am not sure if this is a recommended solution, but you could try setting the output method to xml, and then just using the xsl:copy-of function.
So, the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
<xsl:template match="//Book">
<xsl:value-of select="Title" />
<xsl:text>|</xsl:text>
<xsl:value-of select="Author" />
<xsl:text>|</xsl:text>
<xsl:copy-of select="." />
<!-- put in the newline -->
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, generates the following output
Python Does Everythig|Smith|<Book id="123"><Title>Python Does Everythig</Title><Author>Smith</Author></Book>
Postgres is Neat|Wesson|<Book id="456"><Title>Postgres is Neat</Title><Author>Wesson</Author></Book>
Try that :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="//Book"/>
</xsl:template>
<xsl:template match="Book">
<xsl:value-of select="Title" />
<xsl:text>|</xsl:text>
<xsl:value-of select="Author" />
<xsl:text>|</xsl:text>
<xsl:apply-templates select="." mode="outputTags"/>
</xsl:template>
<xsl:template match="*" mode="outputTags">
<xsl:text><</xsl:text>
<xsl:value-of select="local-name()"/>
<xsl:apply-templates select="#*"/>
<xsl:text>></xsl:text>
<xsl:apply-templates mode="outputTags"/>
<xsl:text></</xsl:text>
<xsl:value-of select="local-name()"/>
<xsl:text>></xsl:text>
<xsl:if test="self::Book">
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:template>
<xsl:template match="#*">
<xsl:text> </xsl:text>
<xsl:value-of select="local-name()"/>
<xsl:text>="</xsl:text>
<xsl:value-of select="."/>
<xsl:text>"</xsl:text>
</xsl:template>
</xsl:stylesheet>
It produces the following result from your input file :
Python Does Everythig|Smith|<Book id="123"><Title>Python Does Everythig</Title><Author>Smith</Author></Book>
Postgres is Neat|Wesson|<Book id="456"><Title>Postgres is Neat</Title><Author>Wesson</Author></Book>