Mapping a single element to a record using Scripting Functoid - xslt

I am trying to create a mapping between two different elements(Element1 & Element2) from source schema to the same record on Destination Schema along with an element in it as shown in the following format.
<coverages>
//If Element1(Source Schema element) has data
<coverage>
<CoverageTypeCd>Data1</CoverageTypeCd>
</coverage>
//If Element2(Source Schema element) has data
<coverage>
<CoverageTypeCd>Data2</CoverageTypeCd>
</coverage>
</coverages>
I am now trying to check the presence of value on Elements(Source Schema) and then trying to feed it to the scripting functoid. I don’t know how I should produce the entire
<coverage> node and still send the respective <coverageTypeCd> of that record as shown above. I believe it should be done with Inline XSLT. I really need someone’s help on this.
Thanks in advance.
Please let me know if you need additional details on something.
Update
Hi folks, I found the answer myself. I used Inline XSLT Call template as Script Type and it did the job. I applied this script for different elements on Source Schema to map the Coverage record
<xsl:template name="CoverageTemplate1">
<xsl:param name="Element1" />
<xsl:element name="Coverage">
<xsl:element name="CoverageTypeCd">
<xsl:value-of select="$Element1" />
</xsl:element>
</xsl:element>
</xsl:template>
Thanks

I found the answer myself. I used "Inline XSLT Call template" as Script Type and it did the job. I applied this script for different elements on Source Schema to map the Coverage record
<xsl:template name="CoverageTemplate1">
<xsl:param name="Element1" />
<xsl:element name="Coverage">
<xsl:element name="CoverageTypeCd"><xsl:value-of
select="$Element1" /></xsl:element>
</xsl:element>
</xsl:template>
Thanks

Related

XSL getting out of context using dynamic XPATH

I'm trying to reformat an XML I get from an appliance into an HTML table, and it's format is not usual.
It use unique references in node name's, like this:
/network/content/host/content/REF_1/content
/network/content/network/content/REF_2/content
and then, it use the same references to another part of the file, as a value of a content node, like this:
/rules/content/rules/content/REF_3/content/sources/content/name = REF_1
/rules/content/rules/content/REF_3/content/destinations/content/name = REF_2
I'm trying to write a template for content that instead of getting me REF_ID which is unique, I try to get the name, in the other branch leaf. this mean I'm trying to find a value that is out of my actual context.
I'm able to retrieve the name XPATH using this variable:
<xsl:variable name='objName' select="concat('/storage/objects/',#linkclass,'/content/',#linktype,'/content/',current(),'/content/name/content')" />
but, I'm not able to use this XPATH in a query like:
<xsl:value-of select="{$objName}">
I suppose this doesn't work because it's out of context but when I ask statically for one of those XPATH I get the value.
My full code is not very complicated:
<xsl:template match="content">
<xsl:variable name='objXPATH' select="concat('/storage/objects/',#linkclass,'/content/',#linktype,'/content/',current(),'/content/name/content')" />
<xsl:variable name='obj' select="{$objXPATH}" />
<xsl:element name="a">
<xsl:attribute name="href">
#<xsl:value-of select="."/>
</xsl:attribute>
<xsl:value-of select="$obj"/>
<br />
</xsl:element>
</xsl:template>
I need help to fix this, I'm on it since one day with no evolution, and it's driving me crazy. I'm more like a script kiddie than a real developer.
Dynamic evaluation (treating a string in a variable as an XPath expression and evaluating it) is available as a vendor extension in a number of XSLT processors, and it becomes part of the standard with the introduction of xsl:evaluate in XSLT 3.0. If your XSLT processor doesn't have such an extension you may be able to write it yourself. Alternatively, if you explain the problem better, we may be able to suggest a solution that does not require dynamic evaluation.

Change text of elements identified by dynamic XPath

I have an XML with 2 XML fragments, 1st one is a fragment where the new values must be applied (which can have pretty complex elements) like
... some static parents
<a:element1>
<a:subelement tag="someString">
<a:s1>a</a:s1>
</a:subelement>
</a:element1>
<a:element2>b</a:element2>
<a:element3>c</a:element3>
... lots of other elements like the above ones
and 2nd fragment that has XPaths generated from the first XML and a new value, like
<field>
<xpath>/Parent/element1/subelement[#tag="someString"]/s1</xpath>
<newValue>1</newValue>
</field>
<field>
<xpath>/Parent/element2</xpath>
<newValue>2</newValue>
</field>
We might not have new values to apply for all the elements in the first fragment.
I'm struggling to make an XSLT transformation that should apply the new values to the places indicated by the XPaths.
The output should be:
... some static parents
<a:element1>
<a:subelement tag="someString">
<a:s1>1</a:s1>
</a:subelement>
</a:element1>
<a:element2>2</a:element2>
... lots of other elements like the above ones
I have access to xalan:evaluate to evaluate the dynamic xpath. I'm trying different solutions, I will write them here when they will start to make sense.
Any ideas of approaches are well received. Thanks
Oki, I found out how, and I will write the answer here maybe someone sometime will need this:
<xsl:template match="/">
<!-- static parents -->
<a:Root>
<xsl:apply-templates select="/a:Root/a:Parent" />
</a:Root>
</xsl:template>
<xsl:template match="#*|*|text()">
<xsl:variable name="x" select="generate-id(../.)" />
<xsl:variable name="y" select="//field[generate-id(xalan:evaluate(xpath)) = $x]" />
<xsl:choose>
<xsl:when test="$y">
<xsl:value-of select="$y/newValue" />
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|*|text()" />
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And to explain the transformation:
I'm writing down part that is static and then call apply-templates on the fragment I'm interested in, that has a liquid structure.
Then I'm using a slightly modified identity transformation that copies everything from source to target (starting from the /a:Root/a:Parent fragment), except when we position ourselves on the text I'm interested in changing.
The text() I'm interested in will have as parent (../.) the element referred by an xpath string found in the second fragment. Variable x means, in the context of the when, this element.
Variable y finds a field element that has as child an xpath element that if evaluated using xalan will refer to the same element that the x variable relates to.
Now I used generate-id() in order to compare the physical elements, otherwise it would have compared by the toString of the element (which is wrong). If variable y doesn't exist, it means that I have no xpath element for this element that could have changed, and I'm leaving it alone. If the y variable exists, I can get from it the newValue and I'm currently positioned on the element which text I want to update.

Filter and sort table based XML

I am new to xslt and have done some research and read a brief book and looked at many examples but I'm afraid I just don't get it. I've only done simple procedural coding before and I guess I'm missing something. I understand a very basic example but when I try to transform my own data I am completely lost. Boo hoo hoo. It is soooo frustrating knowing that I don't it! I really feel like a hack :(
Anyway, I generated the following XML from a table in MS Word. The table ID and Row and Column IDs of each cell are given so it is possible to know how things relate to each other.
Now I want to present the data in a pick list and basically want to:
1. Filter the data on, say [Name='p_fld_parent_ref' and Value='RM12']. The data with the matching "rows" (i.e. all the nodes with the matching RowID) is what I want.
2. I also want to sort that filtered data on the column (cell) with name [p_fld_date_received]. I included a #dateSerial attribute specifically to make the sorting easier.
In the example data I should get any "rows" of data with a parent reference of 'RM12' sorted by date received. I want to use the data in the [p_fld_quantity_available] cell.
I've wasted about 3 days on this and gotten absolutely nowhere. Normally you start to get somewhere but with xslt I've gotten nowhere. Strange.
Here is one "row" of my data (sorry, I pasted the XML but don't know how to format it for reading - can someone let me know how to display it in the right format? Thanks.):
<Root><Data><Element><Name>p_fld_ref</Name><Key>SKU1</Key><KeyType>P</KeyType><ID>1</ID><Value>SKU1</Value><Description>Ref</Description><Required>True</Required><dataType>SKUn</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>1</ColumnID></Element><Element><Name>p_fld_parent_ref</Name><Key>SKU1</Key><KeyType>F</KeyType><ID>2</ID><Value>RM12</Value><Description>Parent Ref</Description><Required>True</Required><dataType>CPLn,RMn</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>2</ColumnID></Element><Element><Name>p_fld_item_no</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>3</ID><Value>ZRMH06</Value><Description>Item Code</Description><Required>True</Required><dataType>Text</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>3</ColumnID></Element><Element><Name>p_fld_type</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>4</ID><Value>RM</Value><Description>Type</Description><Required>True</Required><dataType>CA or RM</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>4</ColumnID></Element><Element><Name>p_fld_serial_no</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>5</ID><Value>120201</Value><Description>Serial No.</Description><Required>True</Required><dataType>Number</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>5</ColumnID></Element><Element><Name>p_fld_name_1</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>6</ID><Value>Product Name 1</Value><Description>Name 1</Description><Required>True</Required><dataType>Text</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>6</ColumnID></Element><Element><Name>p_fld_name_2</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>7</ID><Value>Product Name 1 Lang 2</Value><Description>Name 2</Description><Required>True</Required><dataType>Text</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>7</ColumnID></Element><Element><Name>p_fld_location</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>8</ID><Value/><Description>Location</Description><Required>True</Required><dataType>Text</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>8</ColumnID></Element><Element><Name>p_fld_receipt_no</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>9</ID><Value/><Description>Receipt No.</Description><Required>True</Required><dataType>Text</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>9</ColumnID></Element><Element><Name>p_fld_supplier</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>10</ID><Value/><Description>Supplier</Description><Required>True</Required><dataType>Text</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>10</ColumnID></Element><Element><Name>p_fld_supplier_ref</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>11</ID><Value/><Description>Supplier Ref</Description><Required>True</Required><dataType>Sn</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>11</ColumnID></Element><Element><Name>p_fld_supplier_batchno</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>12</ID><Value/><Description>Supplier Batch No.</Description><Required>True</Required><dataType>Text</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>12</ColumnID></Element><Element><Name>p_fld_supplier_coa</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>13</ID><Value/><Description>CoA Number</Description><Required>True</Required><dataType>Text</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>13</ColumnID></Element><Element><Name>p_fld_box_sample</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>14</ID><Value/><Description>Box to sample</Description><Required>True</Required><dataType>Text</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>14</ColumnID></Element><Element><Name>p_fld_number_boxes</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>15</ID><Value/><Description>Number of Containers</Description><Required>True</Required><dataType>Number</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>15</ColumnID></Element><Element><Name>p_fld_quantity_total</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>16</ID><Value/><Description>Total Quantity</Description><Required>True</Required><dataType>Number</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>16</ColumnID></Element><Element><Name>p_fld_date_received</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>17</ID><Value dateSerial="20130217000000">17-Feb-2013</Value><Description>Date Received</Description><Required>True</Required><dataType>Date</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>17</ColumnID></Element><Element><Name>p_fld_date_retest</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>18</ID><Value dateSerial="20140217000000">17-Feb-2014</Value><Description>Re-Test Date</Description><Required>True</Required><dataType>Date</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>18</ColumnID></Element><Element><Name>p_fld_date_expire</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>19</ID><Value dateSerial="20160217000000">17-Feb-2016</Value><Description>Expiry Date</Description><Required>True</Required><dataType>Date</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>19</ColumnID></Element><Element><Name>p_fld_date_released</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>20</ID><Value dateSerial="20130217000000">17-Feb-2013</Value><Description>Release Date</Description><Required>True</Required><dataType>Date</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>20</ColumnID></Element><Element><Name>p_fld_quantity_reserved</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>21</ID><Value>20000</Value><Description>Reserved Quantity</Description><Required>True</Required><dataType>Number</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>21</ColumnID></Element><Element><Name>p_fld_quantity_used</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>22</ID><Value>0</Value><Description>Used Quantity</Description><Required>True</Required><dataType>Number</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>22</ColumnID></Element><Element><Name>p_fld_quantity_available</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>23</ID><Value>20000</Value><Description>Available Quantity</Description><Required>True</Required><dataType>Number</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>23</ColumnID></Element><Element><Name>p_fld_status</Name><Key>SKU1</Key><KeyType>D</KeyType><ID>24</ID><Value/><Description>Status</Description><Required>True</Required><dataType>Q, R or X</dataType><parmType>1</parmType><TableID>6</TableID><RowID>3</RowID><ColumnID>24</ColumnID></Element></Root>
Assuming that each row is represented by a <Data> element (incidentally, you're missing the end tag of the <Data> element in this sample), this should do the filter/sorting part:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Root">
<Root>
<xsl:apply-templates
select="Data[Element[Name='p_fld_parent_ref']/Value='RM12']">
<xsl:sort select="Element[Name='p_fld_date_received]/Value/#dateSerial" />
</xsl:apply-templates>
</Root>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You'll need to define a template for how you want your <Data> elements presented, and probably something other than simply re-creating the Root element as I've done above. Something as simple as this might do the trick, if you just want to output text:
<xsl:template match="Data">
<xsl:text>Item no.:</xsl:text>
<xsl:value-of select="Element[Name='p_fld_item_no']/Value" />
<xsl:text> Quantity available:</xsl:text>
<xsl:value-of select="Element[Name='p_fld_quantity_available']/Value" />
<xsl:text>
</xsl:text><!-- New line -->
</xsl:template>
This isn't a complete solution, but hopefully it'll give you enough pointers to figure out the rest.

XPath sorting inline if statement

I've been trying to wrap my head around using XPath and XQuery for this with the help of some previous posts to no avail. Right now I have null child nodes which should just default to ordering at the end of a sort but unfortunately, the sort does not occur at all on these null nodes. As a result I have been trying to find a way to set them to zero during the sorting section. Here is a sample below:
<xsl:for-each select="MyItems/Item">
<xsl:sort select="Order/obj/Number" order="ascending">
I want to do something similar to an inline if statement as part of the sort like in C# below:
foreach(item in MyItems.OrderBy(Order/obj/Exists != false ? Order/obj/Number : 0)
I was using these links: dynamic xpath expression and XSLT transfom with inline if statements to try and understand but I'm still not getting it. Any help is appreciated. I need the solution in XSLT.
Your situation is unclear as you say nothing about the contents of your XML or the nature of your XSLT transform. But it sounds something like you have Item elements with no Order/obj/Number elements to sort on?
I would code that something like this
<xsl:template match="/root">
<xsl:copy>
<xsl:apply-templates select="MyItems/Item[Order/obj/Number]">
<xsl:sort select="Order/obj/Number" />
</xsl:apply-templatesh>
<xsl:apply-templates select="MyItems/Item[not(Order/obj/Number)]" />
</xsl:copy>
</xsl:template>
<xsl:template select="MyItems/Item">
<xsl:copy-of select="current()" />
</xsl:template>
Talking about "null nodes" isn't helpful. It's not a well-defined term. Show us your XML, your desired results and your actual results, and we can help you.
What should happen is that if the select expression in xsl:sort returns an empty sequence/node-set, the effective sort key is a zero-length string, so these items sort before any others (assuming ascending order).

XSLT number only counts instances in current file of a multi-file document

I've been tasked with putting together a book (using XSL FO) from a number of XML-files, now I'm trying to number the figures in this book (simple numbering, no resets at chapters or whatever), my naive approach was to do this
<xsl:template match="figure">
<fo:block xsl:use-attribute-sets="figure">
.. stuff to deal with images ..
<fo:block xsl:use-attribute-sets="figure-caption">
Figure <xsl:number level="any"/>: <xsl:apply-templates/>
</fo:block>
<fo:block xsl:use-attribute-sets="figure-caption">
</xsl:template>
I have an aggregate XML file which selects the files to use using the document() function like so:
<xsl:template match="include">
<xsl:apply-templates select="document(#src)"/>
</xsl:template>
Now, my problem is that number seems to always only count the instances in the current file, which is not what I want (currently, there's only one or two images per file, resulting in all figures being 'Figure 1' or 'Figure 2').
I've considered two approaches, both being essentially two-pass XSLT. First, the straightforward approach, generate an intermediary XML containing the entire book using an identity transform, which I'm reluctant to do for other reasons.
Second, using node-set() extension, which I tried like this
<xsl:template match="include">
<xsl:apply-templates select="ext:node-set(document(#src))"/>
</xsl:template>
but this produced the same result.
Any ideas? Perhaps something which isn't a two-pass transformation? Any help would be greatly appreciated.
The two -pass approach is the more logical and robust one.
One-pass approach is very challenging. One can provide an expression in the value attribute of <xsl:number> and this can be used to sum the "local number" with the maximum accumulated number so far from all previous documents.
However, this requires sequencing the documents (which is something bad in a functional language) and this only works for a flat numbering scheme. In case hierarchical numbering is used (3.4.2), I don't see an easy way to continue from the max number of a previous document.
Due to this considerations, I would definitely merge all documents into one before numbering.
I will also use a two phase transformation. But just for fun, with one include level and no repetition, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vIncludes" select="//include"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="include">
<xsl:apply-templates select="document(#src)"/>
</xsl:template>
<xsl:template match="picture">
<xsl:variable name="vRoot" select="generate-id(/)"/>
<xsl:variable name="vInclude"
select="$vIncludes[
$vRoot = generate-id(document(#src))
]"/>
<xsl:copy>
<xsl:value-of
select="count(
document(
(.|$vInclude)/preceding::include/#src
)//picture |
(.|$vInclude)/preceding::picture
) + 1"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With this input:
<master>
<include src="child3.xml"/>
<block>
<include src="child1.xml"/>
<picture/>
</block>
<include src="child2.xml"/>
<picture/>
</master>
And 'child1.xml'
<child1>
<picture/>
</child1>
And 'child2.xml'
<child2>
<picture/>
</child2>
And 'child3.xml'
<child3>
<picture/>
</child3>
Output:
<master>
<child3>
<picture>1</picture>
</child3>
<block>
<child1>
<picture>2</picture>
</child1>
<picture>3</picture>
</block>
<child2>
<picture>4</picture>
</child2>
<picture>5</picture>
</master>
You could use an ancillary XML documentto keep track of the last figure number and load that file as a document from your stylesheet. Or, if you do not want to manage two output files from the same stylesheet (the "real" FOP output and the figure counter) you can simply load the previous chapter's FOP file and look for the MAX of the figure-caption.
Or you could pass the last figure number as a parameter with default zero and pass the parameter on the command line. The value of this parameter resulting from the parsing of the previous one in the ascending resulting document order.
All these alternatives suppose you are running the transformations in sequence in source document ascending order.
A more structured and robust solution would be to manage transverse document sections such as indices, table of contents and table of figures in as many separate FO documents that would be generated in a "second pass" run with their own XSLT.
I think I would do a prepass which outputs summary information about all the documents in a single XML file, and then use this as a secondary input to the number calculation. The summary information in your case might just be a count of how many figures each document contains, but in many cases it can be useful to hold other information as well such as the IDs of sections that will act as the target of hyperlinks.