The Following XML is given (I don't know how many empty <milestone> elements there are):
<milestone n="a"/>
<seg>bla</seg>
<seg>bla</seg>
<milestone n="b"/>
<seg>bla</seg>
<seg>bla</seg>
Needs to be transformed to
<milestone n="a"/>
<seg n="1">bla</seg>
<seg n="2">bla</seg>
<milestone n="b"/>
<seg n="1">bla</seg>
<seg n="2">bla</seg>
How do I restart counting after I hit an empty element?
Thanks for any help!
Please try the following solution
<?xml version="1.0" encoding="UTF-8"?>
<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 omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="seg">
<xsl:variable name="counter">
<xsl:number from="milestone" level="any"/>
</xsl:variable>
<seg>
<xsl:attribute name="n">
<xsl:value-of select="$counter"/>
</xsl:attribute>
</seg>
</xsl:template>
</xsl:stylesheet>
Related
I am new to xml and am trying to add in a link between two collections by inserting a Position() number which I have added into the StyleCollection. I now need to insert this as a StyleLink into the PackingLineCollection where the StyleNumber is equal to the StyleNumber in the StyleCollection. Can anybody point me in the right direction?
Here is the Input:
<Order>
<UniqueReferenceNumber>Order1234</UniqueReferenceNumber>
<StyleCollection>
<Style>
<StyleNumber>1234</StyleNumber>
<StyleType>
<Code>abc</Code>
</StyleType>
<LocationNumber></LocationNumber>
</Style>
<Style>
<StyleNumber>567</StyleNumber>
<StyleType>
<Code>xyz</Code>
</StyleType>
<LocationNumber></LocationNumber>
</Style>
</StyleCollection>
<SubOrderCollection>
<SubOrder>
<UniqueReferenceNumber>SubOrder1</UniqueReferenceNumber>
<PackingLineCollection>
<PackingLine>
<StyleNumber>1234</StyleNumber>
</PackingLine>
</PackingLineCollection>
</SubOrder>
<SubOrder>
<UniqueReferenceNumber>SubOrder2</UniqueReferenceNumber>
<PackingLineCollection>
<PackingLine>
<StyleNumber>xyz</StyleNumber>
</PackingLine>
</PackingLineCollection>
</SubOrder>
</SubOrderCollection>
</Order>
Here is the xslt:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" mlns: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 ="/">
<Order>
<Reference>
<xsl:value-of select="Order/UniqueReferenceNumber"/>
</Reference>
<StyleCollection>
<xsl:for-each select="Order/StyleCollection/Style">
<Style>
<StyleNumber>
<xsl:value-of select="StyleNumber"/>
</StyleNumber>
<LocationNumber><xsl:value-of select="position()"/>
</LocationNumber>
<StyleType>
<Code>
<xsl:value-of select="StyleType/Code"/>
</Code>
</StyleType>
</Style>
</xsl:for-each>
</StyleCollection>
<SubOrderCollection>
<xsl:for-each select="Order/SubOrderCollection/SubOrder">
<SubOrder>
<Reference><xsl:value-of select="UniqueReferenceNumber"/>
</Reference>
<PackingLineCollection>
<xsl:for-each select="PackingLineCollection/PackingLine">
<PackingLine>
<StyleNumber>
<xsl:value-of select="StyleNumber" />
</StyleNumber>
<StyleLink><xsl:value-of
Select="Order/StyleCollection/Style/LocationNumber"/></StyleLink>
</PackingLine>
</xsl:for-each>
</PackingLineCollection>
</SubOrder>
</xsl:for-each>
</SubOrderCollection>
</Order>
</xsl:template>
</xsl:stylesheet>
I have implemented it using a key and xsl:number as follows (in XSLT 3 but the key and xsl:number should work in XSLT 1 or 2 as well, you would need to replace the used <xsl:mode on-no-match="shallow-copy"/> with the identity transformation template):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:key name="style-ref" match="StyleCollection/Style" use="StyleNumber"/>
<xsl:template match="Style/LocationNumber">
<xsl:copy>
<xsl:number from="StyleCollection" level="any"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PackingLine/StyleNumber">
<xsl:next-match/>
<xsl:apply-templates select="key('style-ref', .)" mode="count"/>
</xsl:template>
<xsl:template match="Style" mode="count">
<StyleLink>
<xsl:number level="any" from="StyleCollection"/>
</StyleLink>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/nc4NzQt/1 however does not find a matching Style for the second PackingLine/StyleNumber, not sure whether your example data is meant to have no reference there or whether I misunderstood your requirements.
XSLT 1 is
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="style-ref" match="StyleCollection/Style" use="StyleNumber"/>
<xsl:template match="Style/LocationNumber">
<xsl:copy>
<xsl:number from="StyleCollection" level="any"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PackingLine/StyleNumber">
<xsl:call-template name="identity"/>
<xsl:apply-templates select="key('style-ref', .)" mode="count"/>
</xsl:template>
<xsl:template match="Style" mode="count">
<StyleLink>
<xsl:number level="any" from="StyleCollection"/>
</StyleLink>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/nc4NzQt/2
I want to compare the attributes of two xml-Files and identity transform the input file in the same step. The output xml should only contain elements whose attributes occur in the comparing xml. As shown in the given example, the last concept node should not be outputted, as there is no matching attribute in the comparing.xml
input.xml
<navigation
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<facets>
<facet id="d1e12000000000000000000000011111">
<title xml:lang="en">sometxt</title>
<title xml:lang="de">eintxt</title>
<concepts>
<concept id="d1e12000000000000000000000000000">
<title xml:lang="en">sometxt</title>
<title xml:lang="de">eintxt</title>
<concepts>
<concept id="d1e19000000000000000000000000000">
<title xml:lang="en">sometxt</title>
<title xml:lang="de">eintxt</title>
<concepts>
</concepts>
</concept>
</concepts>
</concept>
</concepts>
</facet>
</facets>
part of comparing.xml with indefinite heading-levels
<foo>
<heading class="d1e12000000000000000000000011111|d1e12000000000000000000000000000">Myheading</heading>
<chapter>
<heading class="d1e12000000000000000000000011111|d1e12000000000000000000000000000">myheading</heading>
<operation>
<heading class="d1e12000000000000000000000011111|d1e12000000000000000000000000000">another heading</heading>
</operation>
</chapter>
desired output.xml with only applicable id's
<nav:navigation
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:nav="http://www.nav.de/">
<nav:facets>
<nav:facet id="d1e12000000000000000000000011111">
<nav:title xml:lang="en">sometxt</nav:title>
<nav:title xml:lang="de">eintxt</nav:title>
<nav:concepts>
<nav:concept id="d1e12000000000000000000000000000">
<nav:title xml:lang="en">sometxt</nav:title>
<nav:title xml:lang="de">eintxt</nav:title>
<nav:concepts>
</nav:concepts>
</nav:concept>
</nav:concepts>
</nav:facet>
</nav:facets>
my xsl so far
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:nav="http://www.nav.de/"
version="2.0" >
<xsl:output method="xml" indent="yes" encoding="utf-8"/>
<xsl:variable name="docu" select="document(comparing.xml)"/>
<xsl:template match="*">
<xsl:element name="nav:{name()}" namespace="http://www.nav.de/">
<xsl:copy-of select="namespace::*"/>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
EDIT: sorry for posting this in the comment-section. I've tried something along those lines, but it didn't work
<xsl:template match="concept | facet">
<xsl:variable name="foo-id" select="#id"/>
<xsl:for-each select="$docu//heading">
<xsl:if test="contains(./#class, $foo-id)">
<xsl:apply-templates/>
</xsl:if>
</xsl:for-each>
</xsl:template>
I would suggest you try it this way:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:nav="http://www.nav.de/">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="comparing-url" select="'comparing.xml'"/>
<xsl:key name="comp" match="#class" use="tokenize(., '\|')" />
<xsl:template match="*">
<xsl:element name="nav:{name()}" >
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*[#id][not(key('comp', #id, document($comparing-url)))]"/>
</xsl:stylesheet>
I am trying to use a key to group <p> elements based on whether they have a specific processing-instruction as a first preceding sibling, but I am having no luck.
For the following example input, I would like each <p> that has a first preceding-sibling processing-instruction that contains “key” to be grouped with its sibling <p> elements that meet the same criteria. Example input:
<root>
<p>not in key</p>
<?samplePI key?>
<p>start of key; </p>
<?samplePI key?>
<p>in key 1; </p>
<?samplePI key?>
<p>in key 2; </p>
<p>Not in key</p>
<?samplePI key?>
<p>start of new key; </p>
<?samplePI key?>
<p>in new key 3;</p>
</root>
Example output:
<root>
<p>not in key</p>
<p>start of key; in key 1; in key 2;</p>
<p>Not in key</p>
<p>start of new key; in new key 3;</p>
</root>
An example of what I've got:
<xsl:template match="root">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="p">
<xsl:choose>
<xsl:when test="preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]][preceding-sibling::p[1][(preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]])]]">
</xsl:when>
<xsl:when test="preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]][preceding-sibling::p[1][not(preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]])]]">
<p><xsl:value-of select="text()"/>
<xsl:apply-templates select="key('nodes', generate-id())" mode="groupedParas"/>
</p>
</xsl:when>
<xsl:otherwise>
<p><xsl:apply-templates/></p>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="p" mode="groupedParas">
<xsl:apply-templates/>
</xsl:template>
<xsl:key name="nodes" match="node()[(self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]])]" use="generate-id((preceding-sibling::p)[last()])"/>
<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
Note that I need the help with getting the correct key syntax, as opposed to generating the desired structure. I need to use XSLT 1.0 for this. Any help appreciated.
With XSLT 2.0 it looks manageable
<?xml version="1.0" encoding="utf-8"?>
<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:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="root">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="boolean(preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]])">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<p>
<xsl:apply-templates select="current-group()/node()"/>
</p>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With XSLT 1.0 my usual approach is sibling recursion but it needs nastily long and convoluted match patterns:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="processing-instruction()[contains(., 'key')]"/>
<xsl:template match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
and preceding-sibling::node()[2][not(self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]])]]">
<p>
<xsl:apply-templates select="." mode="collect"/>
</p>
</xsl:template>
<xsl:template match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
and preceding-sibling::node()[2][self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]]]"/>
<xsl:template match="p" mode="collect">
<xsl:apply-templates/>
<xsl:apply-templates select="following-sibling::node()[2][self::p and preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]" mode="collect"/>
</xsl:template>
</xsl:stylesheet>
And finally, as you seem to want to use a key, a variation of the sibling recursion shown above which uses a key to identity a group of p elements, is as follows:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:key name="collect"
match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
and preceding-sibling::node()[2][self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]]]"
use="generate-id(preceding-sibling::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
and not(preceding-sibling::node()[2][self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]])][1])"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="processing-instruction()[contains(., 'key')]"/>
<xsl:template match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
and preceding-sibling::node()[2][not(self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]])]]">
<p>
<xsl:apply-templates select="./node() | key('collect', generate-id())/node()"/>
</p>
</xsl:template>
<xsl:template match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
and preceding-sibling::node()[2][self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]]]"/>
</xsl:stylesheet>
I want to remove Space and returns in between the elements only for the particular child element (i.e) for 'math' as mentioned below XML. I tried <xsl:strip-space elements="m:math"/>. But it removes space for all the elements. I am in need to retain the space after <style> element and space before <b> element.
Input XML:
<?xml version="1.0"?>
<chapter xmlns="http://www.w3.org/1998/Math/MathML">
<style>This is style</style>
<math display="block">
<munder>
<mi mathvariant="normal">BASE</mi>
<mi mathvariant="normal">under</mi>
</munder>
<mo>=</mo>
<munder>
<mrow>
<munder>
<mi mathvariant="normal">BASE</mi>
<mi mathvariant="normal">under</mi>
</munder>
</mrow>
<mphantom>
<mi mathvariant="normal">under</mi>
</mphantom>
</munder>
</math>
<b>This is bold</b>
</chapter>
XSLT tried:
<xsl:stylesheet version="2.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="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:mml="http://www.w3.org/1998/Math/MathML">
<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:stylesheet>
Required Output:
<?xml version='1.0' encoding='UTF-8'?>
<chapter xmlns="http://www.w3.org/1998/Math/MathML"><style>This is style</style> <math display="block"><munder><mi mathvariant="normal">BASE</mi><mi mathvariant="normal">under</mi></munder><mo>=</mo><munder><mrow><munder><mi mathvariant="normal">BASE</mi><mi mathvariant="normal">under</mi></munder></mrow><mphantom><mi mathvariant="normal">under</mi></mphantom></munder></math> <b>This is bold</b></chapter>
EDIT: Though this answer isn't valid for this question I'm keeping it for reference since I have explained significance of each templates in linearize XML code.. And posting the relevant answer as new answer..
You don't need strip-space, this should do..
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="no" omit-xml-declaration="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space()" />
</xsl:template>
</xsl:stylesheet>
Explanation:
<xsl:output indent="no" omit-xml-declaration="yes"/>
indent = 'no' takes care of removing addition space between two nodes .. omit-xml-declaration removes xml declaration from output XML. This is optional in your case..
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Copies nodes as is.. except without indentation ..
<xsl:template match="text()">
<xsl:value-of select="normalize-space()" />
</xsl:template>
Coverts unwanted whitespace characters to single-space char .. example:
<node>
there is lots of space
</node>
output will be like this:
<node>there is lots of space</node>
The trick is to insert <xsl:text> </xsl:text> wherever you want ..
Here I'm inserting this text (which introduces space) before copying every element that comes under node chapter..
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="no" omit-xml-declaration="no"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[parent::node()[name() = 'chapter']]">
<xsl:text> </xsl:text>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space()" />
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="utf-8"?><chapter xmlns="http://www.w3.org/1998/Math/MathML"> <style>This is style</style> <math display="block"><munder><mi mathvariant="normal">BASE</mi><mi mathvariant="normal">under</mi></munder><mo>=</mo><munder><mrow><munder><mi mathvariant="normal">BASE</mi><mi mathvariant="normal">under</mi></munder></mrow><mphantom><mi mathvariant="normal">under</mi></mphantom></munder></math> <b>This is bold</b></chapter>
You can modify the condition to accommodate the additional space wherever you want..
I am in need to group <math> element and output <math> element only. I tried below XSLT.
Please note that element can occur any where in the document and also the root element may also change
XSLT 1.0 tried:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://www.w3.org/1998/Math/MathML">
<xsl:key name="aKey" match="m:math" use="."/>
<xsl:template match="node()">
<xsl:copy-of select="key('aKey',m:math)"/>
</xsl:template>
</xsl:stylesheet>
Sample XML:
<?xml version="1.0"?>
<chapter xmlns:m="http://www.w3.org/1998/Math/MathML">
<p>This is sample text
<a><math>This is math</math></a></p>
<a>This is a</a>
<math>This is math</math>
<a>This is a</a>
<a>This is a</a>
<b>This is <math>This is math</math>b</b>
<c>This is C</c>
</chapter>
Output Required:
<math>This is math</math>
<math>This is math</math>
<math>This is math</math>
This will do it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" />
<xsl:template match="math | math//*" priority="2">
<xsl:element name="{name()}">
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="math//#* | math//node()">
<xsl:copy />
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
When run on your sample input, this produces:
<math>This is math</math>
<math>This is math</math>
<math>This is math</math>
An approach using keys, which produces the same output:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" />
<xsl:key name="kMath" match="math" use="''" />
<xsl:template match="/">
<xsl:apply-templates select="key('kMath', '')" />
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="#* | node()" priority="-2">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>