I want to group adjacent nodes <speech> with a following-sibling <attrib> with <disp-quote>. Below is the sample XML file:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<sec id="s1">
<title>Introduction</title>
<para>Sample text here</para>
<sec id="s1.1">
<title>Title</title>
<para>Blah blah blah</para>
<speech>
<speaker>PHRYNIA</speaker>
<para>sample speech</para>
</speech>
<speech>
<speaker>PHRYNIA</speaker>
<para>sample speech</para>
</speech>
<speech>
<speaker>JOEL</speaker>
<para>sample reply</para>
</speech>
<attrib>From eternity...</attrib>
<para>Blah blah blah 2</para>
<speech>
<speaker>PHRYNIA</speaker>
<para>sample speech</para>
</speech>
<test>zzzz</test>
<speech>
<speaker>JOEL</speaker>
<para>sample reply</para>
</speech>
<para>Blah blah blah 3</para>
</sec>
<sec id="sec1.22">
<title>aaa</title>
<para>Text</para>
<speech><speaker>Facilitator:</speaker><para>Have you been in <bold>love</bold>?</para></speech>
<speech><speaker>Interviewer:</speaker><para>Yes <italic>I</italic> have been</para></speech>
<speech><speaker>Facilitator:</speaker><para>Honesto, yung totoo?</para></speech>
<attrib>Information source</attrib>
</sec>
</body>
If I use the following template:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<!-- XSLT Template to copy anything, priority="-1" -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//sec">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each-group select="*"
group-adjacent="self::speech or (self::attrib and preceding-sibling::*[1][self::speech])">
<xsl:choose>
<xsl:when test="current-grouping-key() and current-group()[2]">
<disp-quote>
<xsl:copy-of select="current-group()"/>
</disp-quote>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Nothing happens. The stylesheet did not group the speech and attrib nodes. I think this is because of the nested <sec> elements. I have removed
<sec id="s1">
<title>Introduction</title>
<para>Sample text here</para>
and the end tag
</sec>
And it produced the correct results. The correct output should be (with nesting of <sec>):
<?xml version="1.0" encoding="UTF-8"?>
<body>
<sec id="s1">
<title>Introduction</title>
<para>Sample text here</para>
<sec id="s1.1">
<title>Title</title>
<para>Blah blah blah</para>
<disp-quote>
<speech>
<speaker>PHRYNIA</speaker>
<para>sample speech</para>
</speech>
<speech>
<speaker>PHRYNIA</speaker>
<para>sample speech</para>
</speech>
<speech>
<speaker>JOEL</speaker>
<para>sample reply</para>
</speech>
<attrib>From eternity...</attrib>
</disp-quote>
<para>Blah blah blah 2</para>
<speech>
<speaker>PHRYNIA</speaker>
<para>sample speech</para>
</speech>
<test>zzzz</test>
<speech>
<speaker>JOEL</speaker>
<para>sample reply</para>
</speech>
<para>Blah blah blah 3</para>
</sec>
<sec id="sec1.22">
<title>aaa</title>
<para>Text</para>
<disp-quote>
<speech>
<speaker>Facilitator:</speaker>
<para>Have you been in <bold>love</bold>?</para>
</speech>
<speech>
<speaker>Interviewer:</speaker>
<para>Yes <italic>I</italic> have been</para>
</speech>
<speech>
<speaker>Facilitator:</speaker>
<para>Honesto, yung totoo?</para>
</speech>
<attrib>Information source</attrib>
</disp-quote>
</sec>
</body>
The idea here is to group adjacent speech nodes with a following sibling attrib in <disp-quote> tag and leave the rest as is.
Many thanks in advance.
Instead of the two <xsl:copy-of select="current-group()"/> use <xsl:apply-templates select="current-group()"/>.
Related
I have this flat xml. i need to group the contents on h1/title and copy all following para's until next h1/title pattern if exists else add empty para.
Source XML:
<Element>
<h1>
<title>Name1</title>
</h1>
<h1>
<title>Name2</title>
</h1>
<para>Test1</para>
<para>Test2</para>
<h1>
<title>Name3</title>
</h1>
<para>Test3</para>
<para>Test4</para>
</Element>
I want the output like below.
<Element>
<group>
<h1>
<title>Name1</title>
</h1>
<para> </para>
</group>
<group>
<h1>
<title>Name2</title>
</h1>
<para>Test1</para>
<para>Test2</para>
</group>
<group>
<h1>
<title>Name3</title>
</h1>
<para>Test3</para>
<para>Test4</para>
</group>
</Element>
so far I have tried following template, it does not copy following para's.
<xsl:template match="h1">
<group>
<xsl:copy>
<xsl:for-each-group select="*" group-starting-with="h1">
<xsl:choose>
<xsl:when test="self::h1">
<group>
<xsl:apply-templates select="current-group()"/>
<xsl:apply-templates select="following-sibling::para[not(following::h1)]"/>
</group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</group>
</xsl:template>
I would suggest to group the children of the Element element and then of course inside of the for-each-group you can simply check whether there is no second group item to add the empty para:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Element">
<xsl:copy>
<xsl:for-each-group select="*" group-starting-with="h1">
<xsl:choose>
<xsl:when test="self::h1">
<group>
<xsl:apply-templates select="current-group()"/>
<xsl:if test="not(current-group()[2])">
<para/>
</xsl:if>
</group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:transform>
Online at http://xsltransform.net/naZXpX7.
I have a flat xml and I need to copy all matching elements under one element.
In input xml there are title elements which can randomly appear in xml. I want to put them under one element. Any help?
Input:
<root>
<element>
<para>Text 11.</para>
<para>Text 22.</para>
</element>
<title number="1">
<title.block>Title1</title.block>
<para>Text 33.</para>
<para>Text 44.</para>
</title>
<title number="2">
<title.block>Title2</title.block>
</title>
<element1>
<para>Some Text</para>
</element1>
<title number="3">
<title.block>Title2</title.block>
<para>Text 55.</para>
</title>
<result>
<para>Some Text</para>
</result>
</root>
desired output is:
<root>
<element>
<para>Text 11.</para>
<para>Text 22.</para>
</element>
<title.group>
<title number="1">
<title.block>Title1</title.block>
<para>Text 33.</para>
<para>Text 44.</para>
</title>
<title number="2">
<title.block>Title2</title.block>
</title>
<title number="3">
<title.block>Title2</title.block>
<para>Text 55.</para>
</title>
</title.group>
<element1>
<para>Some Text</para>
</element1>
<result>
<para>Some Text</para>
</result>
</root>
Simply write a template for the first title that creates the group and copies that title and all following siblings into the group:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/title[1]">
<title-group>
<xsl:copy-of select="., following-sibling::title"/>
</title-group>
</xsl:template>
<xsl:template match="root/title[position() gt 1]"/>
</xsl:transform>
http://xsltransform.net/a9Giwu
I have an HTML of the following process.
<p class="Ahead">FIRST SECTION</p>
<p class="Text">with a moisture content of 16.34</p>
<p class="Ahead">SECOND SECTION</p>
<p class="Bhead">Second First Section</p>
<p class="Text">with a moisture content of 20.56</p>
<p class="Chead">Second first first section</p>
<p class="Text">with a moisture content of 48.15</p>
The Ahead, Bhead and Chead should be individual group. How it is possible to group it.
The output should be follow below.
<sec>
<title>FIRST SECTION</title>
<p class="Text">with a moisture content of 16.34</p>
</sec>
<sec>
<title>SECOND SECTION</title>
<sec
<title>Second First Section</title>
<p class="Text">with a moisture content of 20.56</p>
<sec>
<title>Second first first section</title>
<p class="Text">with a moisture content of 48.15</p>
</sec>
</sec>
</sec>
Thanks in advance.
Something like this:
<xsl:function name="f:group">
<xsl:param name="level" as="xs:integer"/>
<xsl:param name="population" as="element()*"/>
<xsl:variable name="name" select="('Ahead', 'Bhead', 'Chead')[$level]"/>
<xsl:for-each-group select="$population"
group-starting-with="p[#class=$name]">
<xsl:choose>
<xsl:when test="#class='Text'">
<xsl:copy-of select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="f:group($level+1, current-group())"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
then:
<xsl:template match="/">
<xsl:sequence select="f:group(1, //p)"/>
</xsl:template>
Not tested, just something for you to start with.
This is input xml input.xml
<root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
This is Prop.xml
<?xml version="1.0" encoding="utf-8"?>
<properties>
<code dpsi="0BZG" docid="asdww">HKBL1.0001.lohk.CAP65</code>
<code dpsi="0BZH" docid="navin">HKBL1.0002.aohk.CAP383</code>
<code no="3">345</code>
</properties>
This is desired output
<root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65" dpsi="0BZG" docid="asdww">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
IF prop.xml code/text matches remotelink/#refptid than copy attribute of prop.xml to remotelink otherwise no changes in remotelink.
This is the XSLT I have written. So far I am not getting result for unmatched condition:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlsx="http://www.stylusstudio.com/XSLT/XLSX" xmlns:spml="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:saxon="http://saxon.sf.net/" version="2.0">
<xsl:template match="#*|node()" name="root">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="remotelink[#service='DOC-ID']" name="t-remote">
<xsl:variable name="refptid" select="./#refpt"/>
<xsl:variable name="path" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')"/>
<xsl:for-each select="$path/properties/code">
<xsl:choose>
<xsl:when test="./text()=$refptid">
<xsl:element name="remotelink">
<xsl:attribute name="DOC-ID" select="./#docid"/>
<xsl:attribute name="dpsi" select="./#dpsi"/>
<xsl:attribute name="refpt" select="$refptid"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:if test="./#docrefid"/>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="remotelink[#service='DOC-ID']"> will never never be triggered, there's no service attribute on the <remotelink> element. Moreover, you don't retrieve the correct attribute (refpt instead of refptid)
This XSL transformation should do the job:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlsx="http://www.stylusstudio.com/XSLT/XLSX" xmlns:spml="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:saxon="http://saxon.sf.net/" version="2.0">
<xsl:variable name="props.doc" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')/properties" />
<xsl:template match="#*|node()" name="root">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="remotelink" name="t-remote">
<xsl:variable name="refptid" select="./#refptid"/>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:if test="$props.doc/code[. = $refptid]">
<xsl:apply-templates select="$props.doc/code[. = $refptid]/#*"/>
</xsl:if>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is the result of the transformation:
<?xml version="1.0" encoding="UTF-8"?><root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65" dpsi="0BZG" docid="asdww">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
I would simply define a global parameter or variable <xsl:variable name="path" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')"/>, then set up a key <xsl:key name="prop" match="code" use="."/>, and then use that in a template
<xsl:template match="remotelink[key('prop', #refptid, $path)]">
<xsl:copy>
<xsl:copy-of select="key('prop', #refptid, $path)/#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
that, together with your first template, should suffice.
I'm facing the following problem or challenge.
I've a an element in my source XML which can have 450 characters.
With my xslt I want to transform this into chunks of 75 characters.
...
<T61>
<parentInfo>SomeInfo</parentInfo>
<T86>
<info>abcdefghijklmnopqrstuvwxyz01234567890abcdefghijklmnopqrstuvwxyz01234567890</info>
</T86>
</T61>
...
The output I generate should look something like:
<T31>
<x>abcdefghijklmnopqrstuvwxyz01234567890</x>
</T31>
<T31>
<x>abcdefghijklmnopqrstuvwxyz01234567890</x>
</T31>
In my code I use an template for T61 which does his work.
I thought to create another template for T86 and call this from inside the T61 template but this seems not to work because I've the complete string. I created an function which could split up the string in parts of 75. But the outcome of the function is still the complete string.
I used a function from an earlier post:
<xsl:function name="my:splitItUp" as="xs:string">
<xsl:param name="input" as="xs:string"/>
<xsl:param name="chunk-size" as="xs:integer"/>
<xsl:value-of>
<xsl:for-each-group select="string-to-codepoints($input)" group-by="(position() -1) idiv $chunk-size">
<xsl:sequence select="codepoints-to-string(current-group())"/>
</xsl:for-each-group>
</xsl:value-of>
</xsl:function>
...
<xsl:template match="T86">
<xsl:for-each select="my:splitItUp(info, 75)">
<T31>
<communication>
<xsl:value-of select="." />
</communication>
</T31>
</xsl:for-each>
</xsl:template>
This structure always result in a complete string. In debug I see it split it up but it concatenates the result together. Can I somehow come out of the function?
Best Regards Dirk
Please have a look this XSLT where you need to set <xsl:param name="stringRequired" select="xs:integer(13)"/> to chunk text:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" encoding="utf-8"/>
<xsl:param name="XML">
<info>abcdefghijklmnopqrstuvwxyz01234567890abcdefghijklmnopqrstuvwxyz01234567890</info>
</xsl:param>
<xsl:param name="stringRequired" select="xs:integer(13)"/>
<xsl:param name="XMLLenfgh" select="string-length($XML)"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="$XMLLenfgh gt $stringRequired">
<xsl:call-template name="getPart"/>
</xsl:when>
<xsl:otherwise>
<T31>
<x>
<xsl:value-of select="$XML/info"/>
</x>
</T31>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="getPart">
<xsl:param name="XML" select="$XML"/>
<xsl:param name="stringRequired" select="$stringRequired"/>
<xsl:param name="XMLLenfgh" select="$XMLLenfgh"/>
<xsl:message>
<xsl:value-of select="$XML"/>
</xsl:message>
<xsl:if test="$XMLLenfgh gt $stringRequired">
<T>
<x>
<xsl:value-of select="substring($XML,1,$stringRequired)"/>
</x>
</T>
<xsl:call-template name="getPart">
<xsl:with-param name="XML"
select="substring($XML,string-length(substring($XML,1,$stringRequired)))"/>
<xsl:with-param name="XMLLenfgh"
select="string-length(substring($XML,string-length(substring($XML,1,$stringRequired))))"/>
<xsl:with-param name="stringRequired" select="$stringRequired"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
OUTPUT:
<T xmlns:xs="http://www.w3.org/2001/XMLSchema">
<x>abcdefghijklm</x>
</T>
<T xmlns:xs="http://www.w3.org/2001/XMLSchema">
<x>mnopqrstuvwxy</x>
</T>
<T xmlns:xs="http://www.w3.org/2001/XMLSchema">
<x>yz01234567890</x>
</T>
<T xmlns:xs="http://www.w3.org/2001/XMLSchema">
<x>0abcdefghijkl</x>
</T>
<T xmlns:xs="http://www.w3.org/2001/XMLSchema">
<x>lmnopqrstuvwx</x>
</T>
<T xmlns:xs="http://www.w3.org/2001/XMLSchema">
<x>xyz0123456789</x>
</T>