Concatenate XML in BPEL 2.0 - xslt-1.0

Need your help for a requirement in BPEL 2.0. I have a collection in the below format
<FilesCollection>
<Files>
<transactionid>
<status>
<filename>
<Files>
<FilesCollection>
I would be getting several such collections while traversing through a ForEach loop.
Once I have exited the loop , I need to concatenate all the collections so that finally I get something as below
<FilesCollection>
<Files>
<transactionid>
<status>
<filename>
<Files>
<Files>
<transactionid>
<status>
<filename>
<Files>
<Files>
<transactionid>
<status>
<filename>
<Files>
<FilesCollection>
Please note that the number of FilesCollection element and the number of Files element appearing within it would be dynamic.
Please help me with this.
Thanks
Arijit

As i understand you have multiple FilesCollection in XML document and you want to wrap inside one then you need to do something like this:
Note: Suppose your root element is root in source XML
<?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:template match="root">
<root>
<FilesCollection>
<xsl:copy-of select="FilesCollection/node()"/>
</FilesCollection>
</root>
</xsl:template>
</xsl:stylesheet>

Related

How do I use XSLT to merge XML elements and then structure elements/attributes of the same name/type?

Main issue
I have an XML file which has multiple similar elements with slightly different information. So, lets say I have two elements with the same X attirbute, but different Y attributes. Here is an example:
<FILE>
<ELEMENT>
<ATTRIBUTEX>1</ATTRIBUTEX>
<ATTRIBUTEY>A</ATTRIBUTEY>
</ELEMENT>
<ELEMENT>
<ATTRIBUTEX>1</ATTRIBUTEX>
<ATTRIBUTEY>B</ATTRIBUTEY>
</ELEMENT>
</FILE>
What I want to do is--based on the fact that both elements have the same X attribute--merge them into a single element with one attribute X and multiple child elements (of the same type)--each of which contain each of the different attributes Y. So, for example, I want my file to end up like this:
<FILE>
<ELEMENT>
<ATTRIBUTEX>1</ATTRIBUTEX>
<NEWELEMENT>
<ATTRIBUTEY>A</ATTRIBUTEY>
</NEWELEMENT>
<NEWELEMENT>
<ATTRIBUTEY>B</ATTRIBUTEY>
</NEWELEMENT>
</ELEMENT>
</FILE>
Possible solution?
One possible solution I can think of, but lack the knowledge to execute, is made up of two steps:
Firstly, I could merge all elements which contain the X attribute so that the information is, at the least, in one place. I don't know quite how to do this.
Secdonly, once this is done, this opens me up--in theory--to use my knowledge of XSLT to re-structure the element.
Problem with the possible solution
However, once all elements that containin the X attribute are merged together (first step), I will have a file in which there will be attributes with the same name (specifically, the Y attribute). Here is an example:
<FILE>
<ELEMENT>
<ATTRIBUTEX>1</ATTRIBUTEX>
<ATTRIBUTEY>A</ATTRIBUTEY>
<ATTRIBUTEY>B</ATTRIBUTEY>
</ELEMENT>
</FILE>
What this means is that--at least with my knowledge--when I execute an XSLT file on the above XML (second step), it cannot distinguish between the two Y elements when sorting them into the two new child elements (NEWELEMENT).
So, let's say I execute the following XSLT on the above XML:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="FILE">
<Record>
<xsl:for-each select = "ELEMENT">
<ELEMENT>
<xsl:copy-of select="ATTRIBUTEX"/>
<NEWELEMENT>
<xsl:copy-of select="ATTRIBUTEY"/>
</NEWELEMENT>
<NEWELEMENT>
<xsl:copy-of select="ATTRIBUTEY"/>
</NEWELEMENT>
</ELEMENT>
</xsl:for-each>
</Record>
</xsl:template>
</xsl:stylesheet>
The output is this:
<Record>
<ELEMENT>
<ATTRIBUTEX>1</ATTRIBUTEX>
<NEWELEMENT>
<ATTRIBUTEY>A</ATTRIBUTEY>
<ATTRIBUTEY>B</ATTRIBUTEY>
</NEWELEMENT>
<NEWELEMENT>
<ATTRIBUTEY>A</ATTRIBUTEY>
<ATTRIBUTEY>B</ATTRIBUTEY>
</NEWELEMENT>
</ELEMENT>
</Record>
As you can see, the XSLT has taken all and any ATTRIBUTEY elements and put them into each NEWELEMENT rather than distinguishing between them and placing one ATTRIBUTEY in the first NEWELEMENT, and the second ATTRIBUTEY in the second NEWELEMENT. I am in need of an XSLT file which does just that, and, as stated further up, produces an XML that looks like this:
<FILE>
<ELEMENT>
<ATTRIBUTEX>1</ATTRIBUTEX>
<NEWELEMENT>
<ATTRIBUTEY>A</ATTRIBUTEY>
</NEWELEMENT>
<NEWELEMENT>
<ATTRIBUTEY>B</ATTRIBUTEY>
</NEWELEMENT>
</ELEMENT>
</FILE>
Would anyone be able to help me with this? Any solutions which generate the desired output (above) from the very first example file would be much appreciated! It's a bonus if that solution follows steps one and two. Thanks!
I'm getting your requested output from the XSLT 3.0 below.
Just a couple of notes beforehand:
You mention ATTRIBUTEs but, in fact, everything is an element in your XML document. There are no attributes in the XML sense.
I really don't see the need to create the additional subelements .. you can already have a list of elements of the same name.
Nonetheless, as an exercise in style(sheets), here is a stylesheet that produces your output from your given input:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes" />
<xsl:template match="/FILE" >
<FILE>
<xsl:for-each-group select="*" group-by="name(.)" >
<xsl:variable name="parentName" as="xs:string" select="current-grouping-key()" />
<xsl:variable name="childNamesSameValues" as="xs:string*" >
<xsl:for-each-group select="current-group()/*" group-by="name(.)" >
<xsl:if test="count(distinct-values(current-group()/text())) eq 1">
<xsl:sequence select="current-grouping-key()" />
</xsl:if>
</xsl:for-each-group>
</xsl:variable>
<xsl:element name="{$parentName}" >
<xsl:for-each-group select="current-group()/*" group-by="name(.)" >
<xsl:choose>
<xsl:when test="current-grouping-key() = $childNamesSameValues">
<xsl:copy-of select="current-group()[1]" />
</xsl:when>
<xsl:otherwise >
<xsl:for-each select="current-group()" >
<xsl:element name="NEW{$parentName}" >
<xsl:copy-of select="." />
</xsl:element>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:element>
</xsl:for-each-group>
</FILE>
</xsl:template>
</xsl:stylesheet>

Merge xml tag value by command-line

I am trying to merge publisher and isbn value into title tag using sed. But I can't find any example here that match my requirement. example as below
from this
<book>
<title>The Big Book of Silly Jokes for Kids</title>
<publisher>Rockridge Press</publisher>
<isbn>ISBN-10</isbn>
</book>
to this
<book>
<title>[Rockridge Press ISBN-10] The Big Book of Silly Jokes for Kids</title>
<publisher>Rockridge Press</publisher>
<isbn>ISBN-10</isbn>
</book>
With xmlstarlet:
xml ed -u /book/title -x "concat('[',/book/publisher/text(),' ',/book/isbn,'] ',/book/title)" book.xml
output:
<?xml version="1.0"?>
<book>
<title>[Rockridge Press ISBN-10] The Big Book of Silly Jokes for Kids</title>
<publisher>Rockridge Press</publisher>
<isbn>ISBN-10</isbn>
</book>
EDIT: space added after ']'
The '--inplace' is optional to 'edit file inplace'.
The command line to modify nodes in an XML file can be xmlstarlet, as you say.
You can also write an XSL program, as follows:
book.xsl
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="utf-8"/>
<xsl:template match="title"><!-- the node you want to modify -->
<title>
<xsl:text>[</xsl:text>
<xsl:value-of select="../publisher/text()"/>
<xsl:text> </xsl:text>
<xsl:value-of select="../isbn/text()"/>
<xsl:text>] </xsl:text>
<xsl:value-of select="../title/text()"/>
</title>
</xsl:template>
<xsl:template match="#*|node()"><!-- identity transformation for remaining nodes -->
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Assuming your original file is called book.xml, the XSL program above can be invoked running at the command line:
xsltproc book.xsl book.xml

Handling 0x19 in XSLT 1.0

I have encountered a problem when input xml contains the character 0x19. I have created a demo xslt to reproduce the issue.
My demo xslt looks like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="param1"/>
<xsl:template match="/">
<Value>
<xsl:value-of select="$param1"/>
</Value>
</xsl:template>
</xsl:transform>
I am passing the character 0x19 as param1. The below output gets generated.
<Value></Value>
which is an invalid xml. How can I get it right?
You are correct that
<Value></Value>
is not well-formed XML 1.0 - XML 1.0 does not allow any control characters below U+0020 except U+0009 (tab), U+000A (LF) and U+000D (CR), not even when expressed as numeric character references, so it is simply not possible to include that character in an XML 1.0 document. The processor is wrong to produce that output, it should raise an error to complain that you've tried to insert an illegal character in the output.
However it is well formed XML 1.1, which allows control characters as &# references but not as literals. If your processor supports this (and the donwnstream components that will be receiving your output support it too) then it may be sufficient to add version="1.1" to the xsl:output instruction
<xsl:output method="xml" indent="yes" version="1.1"/>
to tell it to output XML 1.1 instead of XML 1.0.

How to use parameter or variable values as node name?

I am trying to use the value of a parameter or variable as a node name inside a value-of select but so far failed..
So my XML is as below.
<Data>
<Name>John Smith</Name>
<Date>28112012</Date>
<Phone>iphone</Phone>
<Car>BMW</Car>
</Data>
And my incomplete xslt looks like below.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
exclude-result-prefixes="#all">
<xsl:param name="nodename" select="'Name'"/>
<xsl:template match="/Data">
<Output>
<xsl:value-of select="{$nodename}"/>
</Output>
</xsl:template>
</xsl:stylesheet>
Ideally I want the out put to be
<Output>John Smith</Output>
Is there any way I can do this using XSLT?
I want to be able to select appropriate node based on a users choice.
Thanks
SK
A wild guess, let me know if it works:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" exclude-result-prefixes="#all">
<xsl:param name="nodename" select="'Name'"/>
<xsl:template match="/Data">
<Output>
<xsl:value-of select="//*[name()=$nodename]" />
</Output>
</xsl:template>
</xsl:stylesheet>

XSLT-1.0 can a variable be used to access to other nodes?

With a simple XML like this
<value>
<num>
<accession>111</accession>
<sequence>AAA</sequence>
<score>4000</score>
</num>
</value>
I want to know if it is possible to access to a particular node from a node previously stored in a variable. The XSLT code is very short and explains better what I want to say
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/value/num">
<xsl:variable name="node">
<xsl:copy-of select="current()"/>
</xsl:variable>
<root>
<xsl:copy-of select="$node"/>
</root>
</xsl:template>
</xsl:stylesheet>
So I store the node in the variable "node". Then I can print the contents of the node with $node.
(EDIT) XML output
<root>
<num>
<accession>111</accession>
<sequence>AAA</sequence>
<score>4000</score>
</num>
</root>
What I want to do is to print the contents of a sub-node, like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/value/num">
<xsl:variable name="node">
<xsl:copy-of select="current()"/>
</xsl:variable>
<root>
<xsl:copy-of select="$node/accession"/>
</root>
</xsl:template>
</xsl:stylesheet>
But it gives an error (Component returned failure code: 0x80600008 [nsIXSLTProcessor.transformToFragment]) (check here)
(EDIT) The XML that I would want is
<root>
<accession>111</accession>
</root>
NOTE: The question is not how can I get this output. The question is how, using a variable as in the XSLT provided, can I get this output.
(EDIT:SOLVED)
Actually it is possible, but as pointed out in the comments, the value of a variable has to be assigned with the "select" attribute if a node-set is required. So this code was not working since the variable had a tree fragment instead of a node-set stored in it (read more information here)
Thanks!
Try this:
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/value">
<root>
<xsl:for-each select="num">
<xsl:variable name="node" select="current()" />
<xsl:copy-of select="$node/accession" />
</xsl:for-each>
</root>
</xsl:template>
</xsl:transform>
Note that I used xsl:transform instead of xsl:stylesheet. Also, consider using version 2.0 instead of 1.0 if you have a compliant processor, it adds a lot of useful features.
I still don't see your need for a variable, though.