Get value of an attribute, where you don't know the name - xslt

Say if I have some XML with a line such as:
<machine center="10" left="25" right="162" />
and, using XSLT, I want to turn that in to something like:
<measurement type="center">10</measurement>
<measurement type="left">25</measurement>
<measurement type="right">162</measurement>
How do I do that? At the moment I have the following, but am missing one crucial part:
<measurement>
<xsl:for-each select="#">
<xsl:attribute name="type">
<xsl:value-of name="name()">
</xsl:attribute name="type">
<xsl:value-of name="[WHAT_GOES_HERE?]" />
</xsl:for-each>
</measurement>

Try it this way;
<xsl:template match="machine">
<xsl:for-each select="#*">
<measurement type="{name()}">
<xsl:value-of select="." />
</measurement>
</xsl:for-each>
</xsl:template>

This solution is both: simpler/shorter, avoids <xsl:for-exch> and is fully in the spirit of XSLT:
<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="machine">
<xsl:apply-templates select="#*"/>
</xsl:template>
<xsl:template match="machine/#*">
<measurement type="{name()}"><xsl:value-of select="."/></measurement>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<machine center="10" left="25" right="162" />
the wanted, correct result is produced:
<measurement type="center">10</measurement>
<measurement type="left">25</measurement>
<measurement type="right">162</measurement>

A way to achieve this is:
<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="machine">
<xsl:for-each select="#*">
<xsl:element name="measurement">
<xsl:attribute name="type">
<xsl:value-of select="local-name()" />
</xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Related

precalculating node-set via copy-of and accessing ancestors (XSLT 1.0)

I want to precalculate a subtree of nodes in an source XML, and the process them seperately (because I want the subset to be processed in different ways), and access some values from ancestors.
simple example
<numbers count="5">
<number value="1"/>
<number value="2"/>
<number value="3"/>
<number value="4"/>
<number value="5"/>
</numbers>
and lets say I have an xslt (MSXML) to extract the even nodes somehow
<?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="xml" indent="yes"/>
<xsl:template match="/">
<evens>
<xsl:for-each select="numbers/number">
<xsl:choose>
<xsl:when test="#value mod 2 = 0">
<even>
<xsl:attribute name="count">
<xsl:value-of select="../#count"/>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="#value"/>
</xsl:attribute>
</even>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</evens>
</xsl:template>
</xsl:stylesheet>
and we get..
<evens>
<even count="5" value="2" />
<even count="5" value="4" />
</evens>
nice...
but how can I seperate the filtering from the processing so something like...
<?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="xml" indent="yes"/>
<xsl:template name="calculateNodes">
<xsl:for-each select="numbers/number">
<xsl:choose>
<xsl:when test="#value mod 2 = 0">
<xsl:copy-of select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="nodes">
<xsl:call-template name="calculateNodes"/>
</xsl:variable>
<evens>
<xsl:for-each select="msxsl:node-set($nodes)/number">
<even>
<xsl:attribute name="count">
<xsl:value-of select="../#count"/>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="#value"/>
</xsl:attribute>
</even>
</xsl:for-each>
</evens>
</xsl:template>
</xsl:stylesheet>
this gives.
<evens>
<even count="" value="2" />
<even count="" value="4" />
</evens>
so...the ancestors arent copied.
Is there an idiomatic way to get out of this?
A copied node exists on its own, outside of the original tree. In your example, the parent of number is the $nodes variable, which does not have any attributes.
Why don't you do simply:
<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="/numbers">
<xsl:variable name="nodes" select="number[#value mod 2 = 1]"/>
<evens>
<xsl:for-each select="$nodes">
<even count="{../#count}" value="{#value}"/>
</xsl:for-each>
</evens>
</xsl:template>
</xsl:stylesheet>
This way you have a variable containing a reference to the original nodes, instead of a copy. Then you also have access to the original parent. And the content of the variable is a node-set; you don't need to convert it.
this seems to work
<?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="xml" indent="yes"/>
<xsl:template name="calculateNodes">
<xsl:for-each select="numbers/number">
<xsl:choose>
<xsl:when test="#value mod 2 = 0">
<numberWrapper>
<xsl:attribute name="count">
<xsl:value-of select="../#count"/>
</xsl:attribute>
<xsl:copy-of select="."/>
</numberWrapper>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="nodes">
<xsl:call-template name="calculateNodes"/>
</xsl:variable>
<evens>
<xsl:for-each select="msxsl:node-set($nodes)/numberWrapper">
<even>
<xsl:attribute name="count">
<xsl:value-of select="#count"/>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="number/#value"/>
</xsl:attribute>
</even>
</xsl:for-each>
</evens>
</xsl:template>
</xsl:stylesheet>

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

correct xsl template matching syntax

I'm learning XSL and hope to get some help. I want to extract part of the following datasets.xml and output them as tab delimited texts:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<listDatasetsResponse xmlns="http://www.algorithmics.com/schema">
<status>OK</status>
<datasets size="31">
<dataset>
<id>stress_20150910_20150910_160259</id>
<basedOn>Mimm_20150910_20150910_030922</basedOn>
<active>false</active>
<sandbox>true</sandbox>
<ownedBy>admin</ownedBy>
<createdOn>2015-09-10T16:04:24.199-04:00</createdOn>
<createdBy>rtcesupp</createdBy>
<evaluated>true</evaluated>
<stopped>false</stopped>
</dataset>
<dataset>
<id>imm_20150910_20150910_140315</id>
<basedOn>Mimm_20150910_20150910_030922</basedOn>
<active>true</active>
<sandbox>true</sandbox>
<ownedBy>admin</ownedBy>
<createdOn>2015-09-10T14:04:42.696-04:00</createdOn>
<createdBy>rtcesupp</createdBy>
<evaluated>true</evaluated>
<stopped>false</stopped>
</dataset>
</datasets>
</listDatasetsResponse>
here is the XSL I used:
$ vi dataset.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="iso-8859-1"/>
<xsl:strip-space elements="*" />
<xsl:template match="/listDatasetsResponse/datasets/dataset">
<xsl:value-of select="id"/><xsl:text> </xsl:text>
<xsl:choose>
<xsl:when test="active='true'">
<xsl:text>ACTIVE</xsl:text>
</xsl:when>
<xsl:when test="stopped='true'">
<xsl:text>,STOPPED</xsl:text>
</xsl:when>
<xsl:when test="evaluated='true'">
<xsl:text>,EVALUATED</xsl:text>
</xsl:when>
</xsl:choose>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
I expected the result of:
stress_20150910_20150910_160259 EVALUATED
imm_20150910_20150910_140315 ACTIVE,EVALUATED
but what I got is:
OKstress_20150910_20150910_160259Mimm_20150910_20150910_030922falsetrueadmin2015-09-10T16:04:24.199-04:00rtcesupptruefalseimm_20150910_20150910_140315Mimm_20150910_20150910_030922truetrueadmin2015-09-10T14:04:42.696-04:00rtcesupptruefalse
It seemed like the XSL stylesheet was ignored. Could some one point me to correct XSL template matching syntax?
The fundamental problem in your XSL is that none of the XPath expression being used match the element in the source XML. Notice your XML has default namespace declared at the root element :
xmlns="http://www.algorithmics.com/schema"
Descendant elements without explicit prefix and without local default namespace inherits ancestor default namespace implicitly. To match element in namespace, simply declare a prefix that point to the namespace-uri and use that prefix in your XPath expressions, for example :
<xsl:stylesheet .....
xmlns:d="http://www.algorithmics.com/schema">
.....
<xsl:template match="/d:listDatasetsResponse/d:datasets/d:dataset">
<xsl:value-of select="d:id"/><xsl:text> </xsl:text>
.....
</xsl:template>
</xsl:stylesheet>
How about ...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://www.algorithmics.com/schema"
version="1.0" >
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()" />
<xsl:template match="/">
<xsl:apply-templates select="a:listDatasetsResponse/a:datasets/a:dataset" />
</xsl:template>
<xsl:template match="a:dataset">
<xsl:value-of select="a:id"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="a:active|a:stopped|a:evaluated" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="a:active[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:text>ACTIVE</xsl:text>
</xsl:template>
<xsl:template match="a:stopped[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:text>STOPPED</xsl:text>
</xsl:template>
<xsl:template match="a:evaluated[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:text>EVALUATED</xsl:text>
</xsl:template>
</xsl:stylesheet>
... or this version ...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://www.algorithmics.com/schema"
version="1.0" >
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()" />
<xsl:template match="/">
<xsl:apply-templates select="a:listDatasetsResponse/a:datasets/a:dataset" />
</xsl:template>
<xsl:template match="a:dataset">
<xsl:value-of select="a:id"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="a:active|a:stopped|a:evaluated" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="a:active[.='true'] | a:stopped[.='true'] | a:evaluated[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:value-of select="translate( local-name(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
</xsl:template>
</xsl:stylesheet>

Grouping of grouped data

Input:
<persons>
<person name="John" role="Writer"/>
<person name="John" role="Poet"/>
<person name="Jacob" role="Writer"/>
<person name="Jacob" role="Poet"/>
<person name="Joe" role="Poet"/>
</persons>
Expected Output:
<groups>
<group roles="Wriet, Poet" persons="John, Jacob"/>
<group roles="Poet" persons="Joe"/>
</groups>
As in the above example, I first need to group on person names and find everyone's roles. If more than one person is found to have the same set of roles (e.g. both John and Jacob are both Writer and Poet), then I need to group on each set of roles and list the person names.
I can do this for the first level of grouping using Muenchian method or EXSLT set:distinct etc.
<groups>
<group roles="Wriet, Poet" persons="John"/>
<group roles="Wriet, Poet" persons="Jacob"/>
<group roles="Poet" persons="Joe"/>
</groups>
The above was transformed using XSLT 1.0 and EXSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sets="http://exslt.org/sets" extension-element-prefixes="sets">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="persons-by-name" match="person" use="#name"/>
<xsl:template match="persons">
<groups>
<xsl:for-each select="sets:distinct(person/#name)">
<group>
<xsl:attribute name="persons"><xsl:value-of select="."/></xsl:attribute>
<xsl:attribute name="roles">
<xsl:for-each select="key('persons-by-name', .)">
<xsl:value-of select="#role"/>
<xsl:if test="position()!=last()"><xsl:text>, </xsl:text></xsl:if>
</xsl:for-each>
</xsl:attribute>
</group>
</xsl:for-each>
</groups>
</xsl:template>
</xsl:stylesheet>
However, I need help to understand how to group on the grouped roles.
If XSLT 1.0 solution is not available, please feel free to recommend XSLT 2.0 approach.
Try it this way?
XSLT 1.0
(using EXSLT node-set() and distinct() functions)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="exsl set">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:key name="person-by-name" match="person" use="#name" />
<xsl:key name="person-by-roles" match="person" use="#roles" />
<xsl:variable name="distinct-persons">
<xsl:for-each select="set:distinct(/persons/person/#name)">
<person name="{.}">
<xsl:attribute name="roles">
<xsl:for-each select="key('person-by-name', .)/#role">
<xsl:sort/>
<xsl:value-of select="." />
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</person>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<groups>
<xsl:for-each select="set:distinct(exsl:node-set($distinct-persons)/person/#roles)">
<group roles="{.}">
<xsl:attribute name="names">
<xsl:for-each select="key('person-by-roles', .)/#name">
<xsl:value-of select="." />
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</group>
</xsl:for-each>
</groups>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<groups>
<group roles="Poet, Writer" names="John, Jacob"/>
<group roles="Poet" names="Joe"/>
</groups>
I did exactly the same thing as you already did and then went a step further and grouped again. Now I get the following output with your input:
<?xml version="1.0" encoding="UTF-8"?>
<groups>
<group roles="Writer,Poet" persons="John,Jacob"/>
<group roles="Poet" persons="Joe"/>
</groups>
This is the XSLT 2.0
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns="" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:avintis="http://www.avintis.com/esb" exclude-result-prefixes="#all" version="2.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/persons">
<groups>
<xsl:variable name="persons" select="."/>
<!-- create a temporary variable containing all roles of a person -->
<xsl:variable name="roles">
<xsl:for-each select="distinct-values(person/#name)">
<xsl:sort select="."/>
<xsl:variable name="name" select="."/>
<xsl:element name="group">
<xsl:attribute name="roles">
<!-- sort the roles of each person -->
<xsl:variable name="rolesSorted">
<xsl:for-each select="$persons/person[#name=$name]">
<xsl:sort select="#role"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="string-join($rolesSorted/person/#role,',')"/>
</xsl:attribute>
<xsl:attribute name="persons" select="."/>
</xsl:element>
</xsl:for-each>
</xsl:variable>
<!-- now loop again over all roles of the persons and group persons having the same roles -->
<xsl:for-each select="distinct-values($roles/group/#roles)">
<xsl:element name="group">
<xsl:variable name="name" select="."/>
<xsl:attribute name="roles" select="$name"/>
<xsl:attribute name="persons">
<xsl:value-of select="string-join($roles/group[#roles=$name]/#persons,',')"/>
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</groups>
</xsl:template>
<xsl:template match="*|text()|#*">
<xsl:copy>
<xsl:apply-templates select="*|text()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The roles get also sorted - so independent from the input order of the roles and persons.

How to include the node XML in my XSLT text output?

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>