Split xml to several output files - xslt

Simplistically, I have xml that contains 120 nodes. How can I create 3 xml files that have 50 nodes in each? I've marked output as dynamic. Then I've tried to apply auto-number function but I can't get when it fires and how to create condition on it. What I need is sthm like trigger that would cause creation of new file. My strategy:
P.S. I'm noob at MapForce.

Assuming your input is
<root>
<elt>...</elt>
...
</root>
then simplistically, you could do something like:
<xsl:template match="/">
<xsl:document href="1-50.xml">
<root>
<xsl:for-each select="root/elt[pos() <= 50]">
<xsl:copy-of select="."/>
</xsl:for-each>
</root>
</xsl:document>
<xsl:document href="51-100.xml">
<root>
<xsl:for-each select="root/elt[pos() >= 51 and pos() <= 100]">
<xsl:copy-of select="."/>
</xsl:for-each>
</root>
</xsl:document>
<!-- repeat for other portions of input -->
</xsl:template>
In practice, you'd want to be a little bit smarter to handle arbitrary numbers of nodes in the input.

Related

How to compare (partial) subtrees of an XML document using XSLT, returning a boolean value based on the comparison as a single tag?

Assume the existence of a software system that enables the use of XSLTs to specify a certain predicate on an XML message. Specifically: transform an input document to an output document of the following form: <predicate>true</predicate> (or <predicate>false</predicate>).
For some simple cases (like message contains XPath) this is rather trivial, but I now need write an XSLT for something like the following:
<change>
<!-- state before change -->
<item>
<name>
<first>...</first>
<last>...</last>
</name>
<something>
...
</something>
</item>
<!-- state after change -->
<item>
<name>
<first>...</first>
<last>...</last>
</name>
<something>
...
</something>
</item>
</change>
And I would like to return <predicate>true</predicate> for a definition of a mutation if:
The before or after state (or both) actually contain a subtree of something data (as this part is optional), so basically change/item[1]/something | change/item[2]/something, and
The before and the after state with both having any something data removed are not identical to each other.
The second part could be something like the following pseudocode: $before variable is change/item[1]/something with any existing something subtree removed from it, $after variable is change/item[2]/something with any existing something subtree removed from it and then perhaps something like not(deep-equal($before,$after))...?
Anyone here have any idea how I could do this using XSLT 2.0, as I suspect this to be totally impossible in XSLT 1.0?
Try along the lines of
<xsl:template match="change">
<xsl:choose>
<xsl:when test="item[1]/something | item[2]/something">
<xsl:variable name="before" as="element(item)">
<xsl:apply-templates select="item[1]" mode="rs"/>
</xsl:variable>
<xsl:variable name="after" as="element(item)">
<xsl:apply-templates select="item[2]" mode="rs"/>
</xsl:variable>
<predicate><xsl:value-of select="not(deep-equal($before,$after))"/></predicate>
</xsl:when>
<xsl:otherwise>
<predicate>false</predicate>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#* | node()" mode="rs">
<xsl:copy>
<xsl:apply-templates select="#* , node()" mode="rs"/>
</xsl:copy>
</xsl:template>
<xsl:template match="something" mode="rs"/>
Untested but should give you an idea. It basically runs a transformation in mode="rs" (remove-something) on the two item elements to strip the something element, then it does the comparison you posted (not(deep-equal($before,$after))).

Transform tag only if other kind of tag will follow

I'd like to amend an existing xsl file so that the tag <Empty> in the source document is only transformed if there will follow another sibling which is not this tag. A kind of truncate of these tags to the end.
Currently I have:
...
<xsl:template match="Expression">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="Empty">
<xsl:value-of select="."/>
</xsl:template>
Can this be achieved with xslt?
Sample input
<root>
<dummy1>Test1</dummy1>
<Empty>Empty1</Empty>
<Empty>Empty2</Empty>
<dummy2>Test2</dummy2>
<Empty>Empty3</Empty>
<Empty>Empty4</Empty>
<Empty>Empty5</Empty>
</root>
desired output:
Test1Empty1Empty2Test2
Please post a small input sample and the corresponding result you want to create with XSLT. There is a sibling axis in XPath so doing e.g.
<xsl:template match="Empty[following-sibling::*[1][not(self::Empty)]]">
<!-- now transform here as needed -->
</xsl:template>
matches any Empty elements followed by another element that is not an Empty element but I am not sure that is all you need and what you want to do inside of the template.
Assumed Input XML as:
<?xml version="1.0" encoding="utf-8"?>
<root>
<dummy1>test</dummy1>
<Empty>do-not-copy</Empty>
<Empty>copy-this1</Empty>
<dummy2>test</dummy2>
<Empty>do-not-copy-this2</Empty>
</root>
In the above XML .. 2nd element <Empty>do-not-copy</Empty> has immediate next sibling as <Empty>copy-this1</Empty> so it should not be selected. where as 3rd element is followed-by <dummy2/> so it should be copied ..
and 5th element is <Empty>copy-this2</Empty> it's not followed by any tag .. so it should be dropped as well :)
this is the XSL code for that:
<xsl:template match="Empty[following-sibling::*[1][name()!='Empty']]">
<xsl:value-of select="."/>
</xsl:template>
I am adding up a new answer ..
If this is your XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<dummy>test</dummy>
<Empty>copy-this</Empty>
<Empty>copy-this1</Empty>
<dummy>test</dummy>
<Empty>copy-this</Empty>
<Empty>copy-this1</Empty>
<dummy>test</dummy>
<Empty>donot-copy-this2</Empty>
<Empty>donot-copy-this2</Empty>
</root>
XSLT: sample code..
<xsl:template match="dummy">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="Empty[following-sibling::*[name()!='Empty']]">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="text()"/>
Output:
testcopy-thiscopy-this1testcopy-thiscopy-this1test
I modified the proposed solutions to the following:
<xsl:template match="Empty[following-sibling::*[not(self::Empty)]]">
<xsl:value-of select="."/>
</xsl:template>
which seems to work.

XSL outputting more than expected

I'm fairly new to XSLT and XPath and have been banging my head against the wall for a while on this problem.
I have the following XML:
<reply>
<multi-results>
<multi-item>
<name>node1</name>
<information>
<block>
<slot>A</slot>
<state>Online</state>
<colour>purple</colour>
</block>
<block>
<slot>B</slot>
<state>Online</state>
<colour>yellow</colour>
</block>
<block>
<slot>C</slot>
<state>Online</state>
<colour>red</colour>
</block>
<block>
<slot>D</slot>
<state>Online</state>
<colour>blue</colour>
</block>
</information>
</multi-item>
</multi-results>
<address>
<label>this is an arbitrary bit of text included for this example</label>
</address>
</reply>
There are a variable number of "block" entries per file.
I want to "CSV" the data, and I'm using the following XSL:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="*/text()[normalize-space()]">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
<xsl:template match="*/text()[not(normalize-space())]" />
<xsl:template match="block">
<xsl:value-of select="slot"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="state"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="colour"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Output:
node1A|Online|purple
B|Online|yellow
C|Online|red
D|Online|blue
this is an arbitrary bit of text included for this example
However, the output includes both the "name" and the "label"...
I want only what I'm explicitly asking for in the XSL:
A|Online|purple
B|Online|yellow
C|Online|red
D|Online|blue
I don't understand why. Can someone explain please?
Also, there may be multiple "name" elements, each with its own number of "block" elements.
Many thanks in advance
The elements outside the <block> are being processed using the default template rules. To prevent this you need to add
<xsl:template match="/">
<xsl:apply-templates select="block"/>
</xsl:template>
Then you don't need the template rules that match text nodes, because you never apply-templates to text nodes.
Just remove xsl:value-of from your first xsl:template. You get "name" and "label" contents because of it: it takes any text node and outputs its content. Moreover you don't need checking conditions on text nodes, leave one xsl:template for them with empty body:
<xsl:template match="*/text()"/>

How to count output lines with xslt?

I'm outputting text with XSLT. I need to count the number of lines of each section and write it out to my output file. How this could be done?
My output is like this:
HDR+aaa:bbb'
AAA+78901234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+112211'
DDD+121122'
XXX+number_of_records+78901234567890'
AAA+1234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+1212:2121'
BBB+123+String'
BBB+122+String'
CCC+String'
XXX+number_of_records+1234567890'
The number_of_records should contain number of lines from AAA to XXX including both lines. In the first section the number of lines should be 6 and in the second message it should be 8. The first and the last line of each section will share the same unique ID-number.
The number of lines cannot be counted from source since there is so much processing inside XSLT.
A conceptually simple way to do this would be to use a second-stage process. Take the output of your initial transformation (what you posted) and run it through a template (or stylesheet like #Alejandro's) that parses it into lines, and groups the lines starting with AAA... and ending with XXX. See Up-conversion using XSLT 2.0 for a very clear and practical tutorial on doing this, using tokenize(), xsl:analyze-string, and xsl:for-each-group. Then count the lines in each group, and re-output each line, plug the line count into the XXX record.
But that's inefficient, and somewhat error-prone, since you would be parsing the initial output. Why parse a serialization of information that the stylesheet already had internally? You could avoid the inefficiency by changing your initial output to XML, something like
<hdr>
<section id="78901234567890">
<!-- It sounds like AAA's ID actually applies to the section? -->
<AAA String1="..." String2="..."/>
<BBB .../>
<!-- no need to include XXX at this stage AFAICT -->
</section>
<section id="1234567890">
...
</section>
</hdr>
Then the second-stage template (or a separate stylesheet) could take this XML as input and very easily serialize it as you have done above, counting the lines as it goes.
In XSLT 1.0, you would have to use a separate stylesheet to process the output XML, or else use the extension function node-set(). (But even with a separate stylesheet processor, you could still avoid the cost of re-parsing the intermediate XML, if you can pipeline the two stylesheet processors together using SAX.) In XSLT 2.0, you can process the XML output of one template with another template, without restriction.
Just for fun, until you post your input sample and stylesheet building that text output, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="result" name="result">
<xsl:param name="pString" select="."/>
<xsl:variable name="vAfter" select="substring-after($pString, 'AAA+')"/>
<xsl:choose>
<xsl:when test="$vAfter!=''">
<xsl:variable name="vId"
select="substring-before($vAfter, '+')"/>
<xsl:variable name="vEnd"
select='concat("XXX+number_of_records+",$vId,"&apos;
")'/>
<xsl:variable name="vInto"
select="substring-before($vAfter,$vEnd)"/>
<xsl:value-of
select='concat(substring-before($pString,"AAA+"),
"AAA+",
$vInto,
"XXX+",
string-length(translate($vInto,
translate($vInto,
"
",
""),
"")) + 1,
"+",$vId,"&apos;
")'/>
<xsl:call-template name="result">
<xsl:with-param name="pString"
select="substring-after($vAfter,$vEnd)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pString"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
With this input:
<result>
HDR+aaa:bbb'
AAA+78901234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+112211'
DDD+121122'
XXX+number_of_records+78901234567890'
AAA+1234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+1212:2121'
BBB+123+String'
BBB+122+String'
CCC+String'
XXX+number_of_records+1234567890'
</result>
Output:
HDR+aaa:bbb'
AAA+78901234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+112211'
DDD+121122'
XXX+6+78901234567890'
AAA+1234567890+String1:String2'
BBB+123+String'
CCC+321:1212'
DDD+1212:2121'
BBB+123+String'
BBB+122+String'
CCC+String'
XXX+8+1234567890'
My solution: I created an extension function that increments number_of_records by one each time I call it. I use xsl:comment to suppress the output until I really need to output the number. I reset the number_of_records after each XXX+ -line.
Doing this in two steps vould have caused too much hassle.

Efficient code for replacing a text node with some other text using XSLT?

I have an XML where a text node with value "null tag" appears (randomly) at the different places in the file.
My question is how to replace the text with some other text, where the element (and parent node) name is unknown. I have already created an XSLT file which looks a bit bulky and I am not sure about its efficiency in transformation time.
This is the sample test XML I have created:
<root>
<sub_root>abc</sub_root>
<sub_root>
<child>test value</child>
<child2>test value</child2>
<sub_child>
<node1>data</node1>
<node2>data2</node2>
<node3>
<grand_child>test value</grand_child>
</node3>
<node4>test value</node4>
</sub_child>
</sub_root>
</root>
This is the XSLT :
<xsl:template match="#*|*|text()">
<xsl:copy>
<xsl:choose>
<xsl:when test="text()='test value'">
<xsl:apply-templates select="#*|*"/>
<xsl:text>replaced</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="#*|*|text()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
This is the desired output, I am trying to generate:
<root>
<sub_root>abc</sub_root>
<sub_root>
<child>replaced</child>
<child2>replaced</child2>
<sub_child>
<node1>data</node1>
<node2>data2</node2>
<node3>
<grand_child>replaced</grand_child>
</node3>
<node4>replaced</node4>
</sub_child>
</sub_root>
</root>
can this code be written in yet more better way(in any terms)? Or is that my code is better enough?
Just add this to the identity transform:
<xsl:template match="text()[. = 'test value']">
<xsl:text>replaced</xsl:text>
</xsl:template>
The result will copy every node in the input, unmodified, to the output - except for text nodes whose value is test value, which will be transformed into text nodes with the value replaced.