XSL to produce all element and attribute names










0















I need two XSL style sheets that evaluate any XML document and returns:



1) all element names within their hierarchy;



2) all attribute names associated with each element; and



3) one style sheet that output the result to XML; one style sheet that outputs the result to text.



I will transform this in SSIS, which I have done quite a few times including with basic XSL style sheets that I've created. I will be loading this into a table within a database.



Please note that I possess minimal knowledge of XSL/XML. I may use incorrect terms. Also I may overlook something obvious in my request. Therefore I would rely on you to apply your insights.



Example XML:



<BOOK id="1" chapters="9">
<AUTHOR gender="Male" age="43">
<NAME>John Smith</NAME>
</AUTHOR>
<TITLE>Just a book</TITLE>
</BOOK>


Desired text output (two columns delimited by tab or some character):



element attribute
/BOOK id
/BOOK chapters
/BOOK/AUTHOR gender
/BOOK/AUTHOR age
/BOOK/AUTHOR/NAME
/BOOK/TITLE


Desired XML output (more or less? not sure; open to suggestions):



<ROOT>
<ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>id</ATTRIBUTE>
<ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>chapters</ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>gender<ATTRIBUTE></ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>age<ATTRIBUTE></ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
<ELEMENT>/BOOK/TITLE</ELEMENT>
</ROOT>


I don't want to be a mooch and this is more than a little question so I would be willing to pay for the answer.



Thanks.










share|improve this question






















  • You haven't stated it in the requirement, but my guess is that you only want distinct element/attribute name pairs. @MadsHansen interpreted your requirements differently, which just goes to show how careful you need to be with requirements.

    – Michael Kay
    Nov 15 '18 at 8:17















0















I need two XSL style sheets that evaluate any XML document and returns:



1) all element names within their hierarchy;



2) all attribute names associated with each element; and



3) one style sheet that output the result to XML; one style sheet that outputs the result to text.



I will transform this in SSIS, which I have done quite a few times including with basic XSL style sheets that I've created. I will be loading this into a table within a database.



Please note that I possess minimal knowledge of XSL/XML. I may use incorrect terms. Also I may overlook something obvious in my request. Therefore I would rely on you to apply your insights.



Example XML:



<BOOK id="1" chapters="9">
<AUTHOR gender="Male" age="43">
<NAME>John Smith</NAME>
</AUTHOR>
<TITLE>Just a book</TITLE>
</BOOK>


Desired text output (two columns delimited by tab or some character):



element attribute
/BOOK id
/BOOK chapters
/BOOK/AUTHOR gender
/BOOK/AUTHOR age
/BOOK/AUTHOR/NAME
/BOOK/TITLE


Desired XML output (more or less? not sure; open to suggestions):



<ROOT>
<ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>id</ATTRIBUTE>
<ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>chapters</ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>gender<ATTRIBUTE></ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>age<ATTRIBUTE></ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
<ELEMENT>/BOOK/TITLE</ELEMENT>
</ROOT>


I don't want to be a mooch and this is more than a little question so I would be willing to pay for the answer.



Thanks.










share|improve this question






















  • You haven't stated it in the requirement, but my guess is that you only want distinct element/attribute name pairs. @MadsHansen interpreted your requirements differently, which just goes to show how careful you need to be with requirements.

    – Michael Kay
    Nov 15 '18 at 8:17













0












0








0








I need two XSL style sheets that evaluate any XML document and returns:



1) all element names within their hierarchy;



2) all attribute names associated with each element; and



3) one style sheet that output the result to XML; one style sheet that outputs the result to text.



I will transform this in SSIS, which I have done quite a few times including with basic XSL style sheets that I've created. I will be loading this into a table within a database.



Please note that I possess minimal knowledge of XSL/XML. I may use incorrect terms. Also I may overlook something obvious in my request. Therefore I would rely on you to apply your insights.



Example XML:



<BOOK id="1" chapters="9">
<AUTHOR gender="Male" age="43">
<NAME>John Smith</NAME>
</AUTHOR>
<TITLE>Just a book</TITLE>
</BOOK>


Desired text output (two columns delimited by tab or some character):



element attribute
/BOOK id
/BOOK chapters
/BOOK/AUTHOR gender
/BOOK/AUTHOR age
/BOOK/AUTHOR/NAME
/BOOK/TITLE


Desired XML output (more or less? not sure; open to suggestions):



<ROOT>
<ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>id</ATTRIBUTE>
<ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>chapters</ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>gender<ATTRIBUTE></ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>age<ATTRIBUTE></ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
<ELEMENT>/BOOK/TITLE</ELEMENT>
</ROOT>


I don't want to be a mooch and this is more than a little question so I would be willing to pay for the answer.



Thanks.










share|improve this question














I need two XSL style sheets that evaluate any XML document and returns:



1) all element names within their hierarchy;



2) all attribute names associated with each element; and



3) one style sheet that output the result to XML; one style sheet that outputs the result to text.



I will transform this in SSIS, which I have done quite a few times including with basic XSL style sheets that I've created. I will be loading this into a table within a database.



Please note that I possess minimal knowledge of XSL/XML. I may use incorrect terms. Also I may overlook something obvious in my request. Therefore I would rely on you to apply your insights.



Example XML:



<BOOK id="1" chapters="9">
<AUTHOR gender="Male" age="43">
<NAME>John Smith</NAME>
</AUTHOR>
<TITLE>Just a book</TITLE>
</BOOK>


Desired text output (two columns delimited by tab or some character):



element attribute
/BOOK id
/BOOK chapters
/BOOK/AUTHOR gender
/BOOK/AUTHOR age
/BOOK/AUTHOR/NAME
/BOOK/TITLE


Desired XML output (more or less? not sure; open to suggestions):



<ROOT>
<ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>id</ATTRIBUTE>
<ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>chapters</ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>gender<ATTRIBUTE></ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>age<ATTRIBUTE></ATTRIBUTE>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
<ELEMENT>/BOOK/TITLE</ELEMENT>
</ROOT>


I don't want to be a mooch and this is more than a little question so I would be willing to pay for the answer.



Thanks.







xml xslt






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 15 '18 at 1:11









digest806digest806

113




113












  • You haven't stated it in the requirement, but my guess is that you only want distinct element/attribute name pairs. @MadsHansen interpreted your requirements differently, which just goes to show how careful you need to be with requirements.

    – Michael Kay
    Nov 15 '18 at 8:17

















  • You haven't stated it in the requirement, but my guess is that you only want distinct element/attribute name pairs. @MadsHansen interpreted your requirements differently, which just goes to show how careful you need to be with requirements.

    – Michael Kay
    Nov 15 '18 at 8:17
















You haven't stated it in the requirement, but my guess is that you only want distinct element/attribute name pairs. @MadsHansen interpreted your requirements differently, which just goes to show how careful you need to be with requirements.

– Michael Kay
Nov 15 '18 at 8:17





You haven't stated it in the requirement, but my guess is that you only want distinct element/attribute name pairs. @MadsHansen interpreted your requirements differently, which just goes to show how careful you need to be with requirements.

– Michael Kay
Nov 15 '18 at 8:17












2 Answers
2






active

oldest

votes


















0














You could generate the desired XML with the following stylesheet:



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>

<xsl:template match="/">
<ROOT>
<xsl:apply-templates select="//*[not(@*)]|//@*" />
</ROOT>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

</xsl:stylesheet>


And then enhance it to be able to generate either the desired XML or text output by setting the value of the format parameter to "text". Setting the format parameter value to anything else will generate the XML output. You can adjust the delimiter value by setting a different value for the delim parameter.



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes" omit-xml-declaration="yes"/>

<xsl:param name="format" select="'text'"/>

<xsl:param name="delim" select="' '"/>

<xsl:template match="/">
<!--generate XML report and assign to a variable-->
<xsl:variable name="report">
<ROOT>
<!--Push elements that don't have attributes, and all attributes.
Elements that have attributes will be transformed when
transforming the attributes -->
<xsl:apply-templates select="//*[not(@*)] | //@*" />
</ROOT>
</xsl:variable>
<!--depending upon the value of the format parameter,
either transform that XML into the text report,
or return the generated XML -->
<xsl:choose>
<xsl:when test="$format='text'">
<xsl:apply-templates select="$report/ROOT"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$report"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<!--for each attribute, generate an ELEMENT with the path and an ATTRIBUTE element-->
<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

<!--templates to transform the generated XML into the text output -->
<xsl:template match="ROOT">
<xsl:sequence select="string-join(('element', 'attribute'), $delim)"/>
<xsl:apply-templates select="ELEMENT"/>
</xsl:template>

<xsl:template match="ELEMENT">
<xsl:value-of select="'
'"/>
<xsl:value-of select="string-join((., following-sibling::*[1][self::ATTRIBUTE]), $delim)"/>
</xsl:template>

</xsl:stylesheet>





share|improve this answer























  • I am grateful for your response. This returned the following error: "Expected end of the expression, found '('.".

    – digest806
    Nov 16 '18 at 23:06











  • This is an XSLT 2.0 solution. It sounds as if you may be using a 1.0 engine. It would be helpful to state in the question if you are limited to XSLT 1.0. Tag it with xslt-1.0

    – Mads Hansen
    Nov 17 '18 at 2:49


















0














Firstly, requirements:



(a) your example doesn't have any instances where there is more than one element or attribute with the same path, so it's not clear whether or not you want to eliminate duplicates.



(b) your output XML is quite tricky to process, because the element paths and attribute names aren't connected except by relative position. It would be better to add a wrapper element, say <PATH>.



(c) in your output, the number of times an element path appears is N where N is the number of attributes found, except that it is 1 when there are no attributes. This seems a little inconsistent to me. I would suggest output of the form



<ROOT>
<PATH>
<ELEMENT>/BOOK</ELEMENT>
<ATTRIBUTE>id</ATTRIBUTE>
<ATTRIBUTE>chapters</ATTRIBUTE>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>
<ATTRIBUTE>gender<ATTRIBUTE>
<ATTRIBUTE>age</ATTRIBUTE>
<PATH>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR/TITLE</ELEMENT>
</PATH>
</ROOT>


If I'm right in thinking that you want to eliminate duplicates, then this is a grouping problem and therefore much easier in XSLT 2.0+. You didn't actually state any constraints on XSLT version, but I'm going to assume XSLT 2.0. Note that there are many XSLT processors that only support 1.0, even though 2.0 has been out for over ten years.



First we want a function that gives the path to an element:



<xsl:function name="f:path" as="xs:string">
<xsl:param name="node" as="node()"/>
<xsl:choose>
<xsl:when test="exists($node/..)">
<xsl:sequence select="concat(f:path($node/..), '/', local-name($node)"/>
</xsl:when>
<xsl:otherwise>/</xsl:otherwise>
</xsl:choose>
</xsl:function>


Now the grouping commences:



<xsl:for-each-group select="//*" group-by="f:path(.)">
<PATH>
<ELEMENT><xsl:value-of select="current-grouping-key()"/></ELEMENT>
<xsl:for-each-group select="current-group()/@*" group-by="local-name()">
<ATTRIBUTE><xsl:value-of select="local-name()"/></ATTRIBUTE>
</xsl:for-each-group>
</PATH>
</xsl:for-each-group>





share|improve this answer























  • I am grateful for your response. This only produced: "John SmithJust a book".

    – digest806
    Nov 16 '18 at 23:04












  • When the result of a transformation is just the text node content of the input, that usually suggests that none of the code in the stylesheet is being executed. I didn't give you a complete stylesheet, just the critical parts; I suspect there's something wrong in the code that you added.

    – Michael Kay
    Nov 17 '18 at 21:55










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53311064%2fxsl-to-produce-all-element-and-attribute-names%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









0














You could generate the desired XML with the following stylesheet:



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>

<xsl:template match="/">
<ROOT>
<xsl:apply-templates select="//*[not(@*)]|//@*" />
</ROOT>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

</xsl:stylesheet>


And then enhance it to be able to generate either the desired XML or text output by setting the value of the format parameter to "text". Setting the format parameter value to anything else will generate the XML output. You can adjust the delimiter value by setting a different value for the delim parameter.



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes" omit-xml-declaration="yes"/>

<xsl:param name="format" select="'text'"/>

<xsl:param name="delim" select="' '"/>

<xsl:template match="/">
<!--generate XML report and assign to a variable-->
<xsl:variable name="report">
<ROOT>
<!--Push elements that don't have attributes, and all attributes.
Elements that have attributes will be transformed when
transforming the attributes -->
<xsl:apply-templates select="//*[not(@*)] | //@*" />
</ROOT>
</xsl:variable>
<!--depending upon the value of the format parameter,
either transform that XML into the text report,
or return the generated XML -->
<xsl:choose>
<xsl:when test="$format='text'">
<xsl:apply-templates select="$report/ROOT"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$report"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<!--for each attribute, generate an ELEMENT with the path and an ATTRIBUTE element-->
<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

<!--templates to transform the generated XML into the text output -->
<xsl:template match="ROOT">
<xsl:sequence select="string-join(('element', 'attribute'), $delim)"/>
<xsl:apply-templates select="ELEMENT"/>
</xsl:template>

<xsl:template match="ELEMENT">
<xsl:value-of select="'
'"/>
<xsl:value-of select="string-join((., following-sibling::*[1][self::ATTRIBUTE]), $delim)"/>
</xsl:template>

</xsl:stylesheet>





share|improve this answer























  • I am grateful for your response. This returned the following error: "Expected end of the expression, found '('.".

    – digest806
    Nov 16 '18 at 23:06











  • This is an XSLT 2.0 solution. It sounds as if you may be using a 1.0 engine. It would be helpful to state in the question if you are limited to XSLT 1.0. Tag it with xslt-1.0

    – Mads Hansen
    Nov 17 '18 at 2:49















0














You could generate the desired XML with the following stylesheet:



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>

<xsl:template match="/">
<ROOT>
<xsl:apply-templates select="//*[not(@*)]|//@*" />
</ROOT>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

</xsl:stylesheet>


And then enhance it to be able to generate either the desired XML or text output by setting the value of the format parameter to "text". Setting the format parameter value to anything else will generate the XML output. You can adjust the delimiter value by setting a different value for the delim parameter.



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes" omit-xml-declaration="yes"/>

<xsl:param name="format" select="'text'"/>

<xsl:param name="delim" select="' '"/>

<xsl:template match="/">
<!--generate XML report and assign to a variable-->
<xsl:variable name="report">
<ROOT>
<!--Push elements that don't have attributes, and all attributes.
Elements that have attributes will be transformed when
transforming the attributes -->
<xsl:apply-templates select="//*[not(@*)] | //@*" />
</ROOT>
</xsl:variable>
<!--depending upon the value of the format parameter,
either transform that XML into the text report,
or return the generated XML -->
<xsl:choose>
<xsl:when test="$format='text'">
<xsl:apply-templates select="$report/ROOT"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$report"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<!--for each attribute, generate an ELEMENT with the path and an ATTRIBUTE element-->
<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

<!--templates to transform the generated XML into the text output -->
<xsl:template match="ROOT">
<xsl:sequence select="string-join(('element', 'attribute'), $delim)"/>
<xsl:apply-templates select="ELEMENT"/>
</xsl:template>

<xsl:template match="ELEMENT">
<xsl:value-of select="'
'"/>
<xsl:value-of select="string-join((., following-sibling::*[1][self::ATTRIBUTE]), $delim)"/>
</xsl:template>

</xsl:stylesheet>





share|improve this answer























  • I am grateful for your response. This returned the following error: "Expected end of the expression, found '('.".

    – digest806
    Nov 16 '18 at 23:06











  • This is an XSLT 2.0 solution. It sounds as if you may be using a 1.0 engine. It would be helpful to state in the question if you are limited to XSLT 1.0. Tag it with xslt-1.0

    – Mads Hansen
    Nov 17 '18 at 2:49













0












0








0







You could generate the desired XML with the following stylesheet:



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>

<xsl:template match="/">
<ROOT>
<xsl:apply-templates select="//*[not(@*)]|//@*" />
</ROOT>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

</xsl:stylesheet>


And then enhance it to be able to generate either the desired XML or text output by setting the value of the format parameter to "text". Setting the format parameter value to anything else will generate the XML output. You can adjust the delimiter value by setting a different value for the delim parameter.



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes" omit-xml-declaration="yes"/>

<xsl:param name="format" select="'text'"/>

<xsl:param name="delim" select="' '"/>

<xsl:template match="/">
<!--generate XML report and assign to a variable-->
<xsl:variable name="report">
<ROOT>
<!--Push elements that don't have attributes, and all attributes.
Elements that have attributes will be transformed when
transforming the attributes -->
<xsl:apply-templates select="//*[not(@*)] | //@*" />
</ROOT>
</xsl:variable>
<!--depending upon the value of the format parameter,
either transform that XML into the text report,
or return the generated XML -->
<xsl:choose>
<xsl:when test="$format='text'">
<xsl:apply-templates select="$report/ROOT"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$report"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<!--for each attribute, generate an ELEMENT with the path and an ATTRIBUTE element-->
<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

<!--templates to transform the generated XML into the text output -->
<xsl:template match="ROOT">
<xsl:sequence select="string-join(('element', 'attribute'), $delim)"/>
<xsl:apply-templates select="ELEMENT"/>
</xsl:template>

<xsl:template match="ELEMENT">
<xsl:value-of select="'
'"/>
<xsl:value-of select="string-join((., following-sibling::*[1][self::ATTRIBUTE]), $delim)"/>
</xsl:template>

</xsl:stylesheet>





share|improve this answer













You could generate the desired XML with the following stylesheet:



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>

<xsl:template match="/">
<ROOT>
<xsl:apply-templates select="//*[not(@*)]|//@*" />
</ROOT>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

</xsl:stylesheet>


And then enhance it to be able to generate either the desired XML or text output by setting the value of the format parameter to "text". Setting the format parameter value to anything else will generate the XML output. You can adjust the delimiter value by setting a different value for the delim parameter.



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes" omit-xml-declaration="yes"/>

<xsl:param name="format" select="'text'"/>

<xsl:param name="delim" select="' '"/>

<xsl:template match="/">
<!--generate XML report and assign to a variable-->
<xsl:variable name="report">
<ROOT>
<!--Push elements that don't have attributes, and all attributes.
Elements that have attributes will be transformed when
transforming the attributes -->
<xsl:apply-templates select="//*[not(@*)] | //@*" />
</ROOT>
</xsl:variable>
<!--depending upon the value of the format parameter,
either transform that XML into the text report,
or return the generated XML -->
<xsl:choose>
<xsl:when test="$format='text'">
<xsl:apply-templates select="$report/ROOT"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="$report"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="*">
<ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
</xsl:template>

<!--for each attribute, generate an ELEMENT with the path and an ATTRIBUTE element-->
<xsl:template match="@*">
<xsl:apply-templates select=".." />
<ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
</xsl:template>

<xsl:template match="*" mode="path">
<xsl:text>/</xsl:text>
<xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
</xsl:template>

<!--templates to transform the generated XML into the text output -->
<xsl:template match="ROOT">
<xsl:sequence select="string-join(('element', 'attribute'), $delim)"/>
<xsl:apply-templates select="ELEMENT"/>
</xsl:template>

<xsl:template match="ELEMENT">
<xsl:value-of select="'
'"/>
<xsl:value-of select="string-join((., following-sibling::*[1][self::ATTRIBUTE]), $delim)"/>
</xsl:template>

</xsl:stylesheet>






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 15 '18 at 3:05









Mads HansenMads Hansen

44.9k1195122




44.9k1195122












  • I am grateful for your response. This returned the following error: "Expected end of the expression, found '('.".

    – digest806
    Nov 16 '18 at 23:06











  • This is an XSLT 2.0 solution. It sounds as if you may be using a 1.0 engine. It would be helpful to state in the question if you are limited to XSLT 1.0. Tag it with xslt-1.0

    – Mads Hansen
    Nov 17 '18 at 2:49

















  • I am grateful for your response. This returned the following error: "Expected end of the expression, found '('.".

    – digest806
    Nov 16 '18 at 23:06











  • This is an XSLT 2.0 solution. It sounds as if you may be using a 1.0 engine. It would be helpful to state in the question if you are limited to XSLT 1.0. Tag it with xslt-1.0

    – Mads Hansen
    Nov 17 '18 at 2:49
















I am grateful for your response. This returned the following error: "Expected end of the expression, found '('.".

– digest806
Nov 16 '18 at 23:06





I am grateful for your response. This returned the following error: "Expected end of the expression, found '('.".

– digest806
Nov 16 '18 at 23:06













This is an XSLT 2.0 solution. It sounds as if you may be using a 1.0 engine. It would be helpful to state in the question if you are limited to XSLT 1.0. Tag it with xslt-1.0

– Mads Hansen
Nov 17 '18 at 2:49





This is an XSLT 2.0 solution. It sounds as if you may be using a 1.0 engine. It would be helpful to state in the question if you are limited to XSLT 1.0. Tag it with xslt-1.0

– Mads Hansen
Nov 17 '18 at 2:49













0














Firstly, requirements:



(a) your example doesn't have any instances where there is more than one element or attribute with the same path, so it's not clear whether or not you want to eliminate duplicates.



(b) your output XML is quite tricky to process, because the element paths and attribute names aren't connected except by relative position. It would be better to add a wrapper element, say <PATH>.



(c) in your output, the number of times an element path appears is N where N is the number of attributes found, except that it is 1 when there are no attributes. This seems a little inconsistent to me. I would suggest output of the form



<ROOT>
<PATH>
<ELEMENT>/BOOK</ELEMENT>
<ATTRIBUTE>id</ATTRIBUTE>
<ATTRIBUTE>chapters</ATTRIBUTE>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>
<ATTRIBUTE>gender<ATTRIBUTE>
<ATTRIBUTE>age</ATTRIBUTE>
<PATH>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR/TITLE</ELEMENT>
</PATH>
</ROOT>


If I'm right in thinking that you want to eliminate duplicates, then this is a grouping problem and therefore much easier in XSLT 2.0+. You didn't actually state any constraints on XSLT version, but I'm going to assume XSLT 2.0. Note that there are many XSLT processors that only support 1.0, even though 2.0 has been out for over ten years.



First we want a function that gives the path to an element:



<xsl:function name="f:path" as="xs:string">
<xsl:param name="node" as="node()"/>
<xsl:choose>
<xsl:when test="exists($node/..)">
<xsl:sequence select="concat(f:path($node/..), '/', local-name($node)"/>
</xsl:when>
<xsl:otherwise>/</xsl:otherwise>
</xsl:choose>
</xsl:function>


Now the grouping commences:



<xsl:for-each-group select="//*" group-by="f:path(.)">
<PATH>
<ELEMENT><xsl:value-of select="current-grouping-key()"/></ELEMENT>
<xsl:for-each-group select="current-group()/@*" group-by="local-name()">
<ATTRIBUTE><xsl:value-of select="local-name()"/></ATTRIBUTE>
</xsl:for-each-group>
</PATH>
</xsl:for-each-group>





share|improve this answer























  • I am grateful for your response. This only produced: "John SmithJust a book".

    – digest806
    Nov 16 '18 at 23:04












  • When the result of a transformation is just the text node content of the input, that usually suggests that none of the code in the stylesheet is being executed. I didn't give you a complete stylesheet, just the critical parts; I suspect there's something wrong in the code that you added.

    – Michael Kay
    Nov 17 '18 at 21:55















0














Firstly, requirements:



(a) your example doesn't have any instances where there is more than one element or attribute with the same path, so it's not clear whether or not you want to eliminate duplicates.



(b) your output XML is quite tricky to process, because the element paths and attribute names aren't connected except by relative position. It would be better to add a wrapper element, say <PATH>.



(c) in your output, the number of times an element path appears is N where N is the number of attributes found, except that it is 1 when there are no attributes. This seems a little inconsistent to me. I would suggest output of the form



<ROOT>
<PATH>
<ELEMENT>/BOOK</ELEMENT>
<ATTRIBUTE>id</ATTRIBUTE>
<ATTRIBUTE>chapters</ATTRIBUTE>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>
<ATTRIBUTE>gender<ATTRIBUTE>
<ATTRIBUTE>age</ATTRIBUTE>
<PATH>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR/TITLE</ELEMENT>
</PATH>
</ROOT>


If I'm right in thinking that you want to eliminate duplicates, then this is a grouping problem and therefore much easier in XSLT 2.0+. You didn't actually state any constraints on XSLT version, but I'm going to assume XSLT 2.0. Note that there are many XSLT processors that only support 1.0, even though 2.0 has been out for over ten years.



First we want a function that gives the path to an element:



<xsl:function name="f:path" as="xs:string">
<xsl:param name="node" as="node()"/>
<xsl:choose>
<xsl:when test="exists($node/..)">
<xsl:sequence select="concat(f:path($node/..), '/', local-name($node)"/>
</xsl:when>
<xsl:otherwise>/</xsl:otherwise>
</xsl:choose>
</xsl:function>


Now the grouping commences:



<xsl:for-each-group select="//*" group-by="f:path(.)">
<PATH>
<ELEMENT><xsl:value-of select="current-grouping-key()"/></ELEMENT>
<xsl:for-each-group select="current-group()/@*" group-by="local-name()">
<ATTRIBUTE><xsl:value-of select="local-name()"/></ATTRIBUTE>
</xsl:for-each-group>
</PATH>
</xsl:for-each-group>





share|improve this answer























  • I am grateful for your response. This only produced: "John SmithJust a book".

    – digest806
    Nov 16 '18 at 23:04












  • When the result of a transformation is just the text node content of the input, that usually suggests that none of the code in the stylesheet is being executed. I didn't give you a complete stylesheet, just the critical parts; I suspect there's something wrong in the code that you added.

    – Michael Kay
    Nov 17 '18 at 21:55













0












0








0







Firstly, requirements:



(a) your example doesn't have any instances where there is more than one element or attribute with the same path, so it's not clear whether or not you want to eliminate duplicates.



(b) your output XML is quite tricky to process, because the element paths and attribute names aren't connected except by relative position. It would be better to add a wrapper element, say <PATH>.



(c) in your output, the number of times an element path appears is N where N is the number of attributes found, except that it is 1 when there are no attributes. This seems a little inconsistent to me. I would suggest output of the form



<ROOT>
<PATH>
<ELEMENT>/BOOK</ELEMENT>
<ATTRIBUTE>id</ATTRIBUTE>
<ATTRIBUTE>chapters</ATTRIBUTE>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>
<ATTRIBUTE>gender<ATTRIBUTE>
<ATTRIBUTE>age</ATTRIBUTE>
<PATH>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR/TITLE</ELEMENT>
</PATH>
</ROOT>


If I'm right in thinking that you want to eliminate duplicates, then this is a grouping problem and therefore much easier in XSLT 2.0+. You didn't actually state any constraints on XSLT version, but I'm going to assume XSLT 2.0. Note that there are many XSLT processors that only support 1.0, even though 2.0 has been out for over ten years.



First we want a function that gives the path to an element:



<xsl:function name="f:path" as="xs:string">
<xsl:param name="node" as="node()"/>
<xsl:choose>
<xsl:when test="exists($node/..)">
<xsl:sequence select="concat(f:path($node/..), '/', local-name($node)"/>
</xsl:when>
<xsl:otherwise>/</xsl:otherwise>
</xsl:choose>
</xsl:function>


Now the grouping commences:



<xsl:for-each-group select="//*" group-by="f:path(.)">
<PATH>
<ELEMENT><xsl:value-of select="current-grouping-key()"/></ELEMENT>
<xsl:for-each-group select="current-group()/@*" group-by="local-name()">
<ATTRIBUTE><xsl:value-of select="local-name()"/></ATTRIBUTE>
</xsl:for-each-group>
</PATH>
</xsl:for-each-group>





share|improve this answer













Firstly, requirements:



(a) your example doesn't have any instances where there is more than one element or attribute with the same path, so it's not clear whether or not you want to eliminate duplicates.



(b) your output XML is quite tricky to process, because the element paths and attribute names aren't connected except by relative position. It would be better to add a wrapper element, say <PATH>.



(c) in your output, the number of times an element path appears is N where N is the number of attributes found, except that it is 1 when there are no attributes. This seems a little inconsistent to me. I would suggest output of the form



<ROOT>
<PATH>
<ELEMENT>/BOOK</ELEMENT>
<ATTRIBUTE>id</ATTRIBUTE>
<ATTRIBUTE>chapters</ATTRIBUTE>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR</ELEMENT>
<ATTRIBUTE>gender<ATTRIBUTE>
<ATTRIBUTE>age</ATTRIBUTE>
<PATH>
<ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
</PATH>
<PATH>
<ELEMENT>/BOOK/AUTHOR/TITLE</ELEMENT>
</PATH>
</ROOT>


If I'm right in thinking that you want to eliminate duplicates, then this is a grouping problem and therefore much easier in XSLT 2.0+. You didn't actually state any constraints on XSLT version, but I'm going to assume XSLT 2.0. Note that there are many XSLT processors that only support 1.0, even though 2.0 has been out for over ten years.



First we want a function that gives the path to an element:



<xsl:function name="f:path" as="xs:string">
<xsl:param name="node" as="node()"/>
<xsl:choose>
<xsl:when test="exists($node/..)">
<xsl:sequence select="concat(f:path($node/..), '/', local-name($node)"/>
</xsl:when>
<xsl:otherwise>/</xsl:otherwise>
</xsl:choose>
</xsl:function>


Now the grouping commences:



<xsl:for-each-group select="//*" group-by="f:path(.)">
<PATH>
<ELEMENT><xsl:value-of select="current-grouping-key()"/></ELEMENT>
<xsl:for-each-group select="current-group()/@*" group-by="local-name()">
<ATTRIBUTE><xsl:value-of select="local-name()"/></ATTRIBUTE>
</xsl:for-each-group>
</PATH>
</xsl:for-each-group>






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 15 '18 at 8:35









Michael KayMichael Kay

111k663119




111k663119












  • I am grateful for your response. This only produced: "John SmithJust a book".

    – digest806
    Nov 16 '18 at 23:04












  • When the result of a transformation is just the text node content of the input, that usually suggests that none of the code in the stylesheet is being executed. I didn't give you a complete stylesheet, just the critical parts; I suspect there's something wrong in the code that you added.

    – Michael Kay
    Nov 17 '18 at 21:55

















  • I am grateful for your response. This only produced: "John SmithJust a book".

    – digest806
    Nov 16 '18 at 23:04












  • When the result of a transformation is just the text node content of the input, that usually suggests that none of the code in the stylesheet is being executed. I didn't give you a complete stylesheet, just the critical parts; I suspect there's something wrong in the code that you added.

    – Michael Kay
    Nov 17 '18 at 21:55
















I am grateful for your response. This only produced: "John SmithJust a book".

– digest806
Nov 16 '18 at 23:04






I am grateful for your response. This only produced: "John SmithJust a book".

– digest806
Nov 16 '18 at 23:04














When the result of a transformation is just the text node content of the input, that usually suggests that none of the code in the stylesheet is being executed. I didn't give you a complete stylesheet, just the critical parts; I suspect there's something wrong in the code that you added.

– Michael Kay
Nov 17 '18 at 21:55





When the result of a transformation is just the text node content of the input, that usually suggests that none of the code in the stylesheet is being executed. I didn't give you a complete stylesheet, just the critical parts; I suspect there's something wrong in the code that you added.

– Michael Kay
Nov 17 '18 at 21:55

















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53311064%2fxsl-to-produce-all-element-and-attribute-names%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

How to how show current date and time by default on contact form 7 in WordPress without taking input from user in datetimepicker

Syphilis

Darth Vader #20