Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Processing specific nodes...‏

Status
Not open for further replies.

TheBernmeister

Programmer
Sep 29, 2013
4
0
0
US
I'm using Apache FOP, embedded in a Java desktop application, to create PDFs from a data XML file and an XSLT file.

Say the data XML file is a list of items of the form

<items>
<item>apple</item>
<item>apples</item>
<item>orange</item>
<item>water melon</item>
...
</items>

I want to be able to tell the XSLT to only process specific nodes (say "apple" and "orange"). Note that each item will have an unique name - no repeats, but some names could contain the names of other nodes (such as apples contains apple).

In the Java code which kicks off the PDF render, I create a java.util.ArrayList and pass that to the transformer using setParameter( "theList", theList ). In the XSLT, when I use <xsl:value-of select="$theList" />, I'll get in the output [apple, orange].

However, what I would need is to take the array and iterate over it, calling a template for each matching item. Is this possible?

Also, given that "apples" contains "apple", using to something like the contains/matches functions would not work. The example data XML is simplified - in reality the items would not necessarily be in English and could contain punctuation, such as "My Item - 1.5, and more"...meaning that using the ',' to (somehow) tokenise would not work either.

Any ideas?


Thanks in advance,

Bernard.
 
Welcome to Tek-Tips, Bernard.

First, I need to ask a few questions.

Is your solution constrained to use a serialized ArrayList? I am not a Java programmer, so I am not sure exactly how ArrayList serializes data that contain commas, but this definitely makes the XSLT side a bit more difficult.

If you can serialize the ArrayList to an XML document, then probably the most straightforward approach is to use that document created from the ArrayList as the input document to the XSLT, and use the document() function to pull information from the data document. This would be my preferred solution because the XSLT is much simpler to create and understand.

If the serialized ArrayList must be processed as a string, then the complexity goes up a bit. If, as I imagine, the serialized ArrayList might contain quoted strings, the complexity of the XSLT to parse the string is not impossible, but not for the beginner either (eee my post regarding scanning JSON using XSLT 1.0 at
So, Bernard, how much flexibility do you have in your solution?


Tom Morrison
Hill Country Software
 
Hi Tom,

No, I'm not constrained to use an ArrayList; I could have just as easily chosen Vector. My thinking is that I need to specify one or more items to process/render (at runtime) and I assumed it would be possible to pass in an array/list of those string items and then in the XSLT loop over that list OR as the entire data XML is processed, the array/list is checked against for matching items.

I've read elsewhere it should be possible to pass an XML nodelist (via Java using setParameter) and then somehow the XSLT can reference the nodelist...but I can't seem to find a concrete example...most posts simply say use a nodeset.

Also, FOP uses XALAN which (I believe) is XSLT 1.0 and so I'm limited to the solutions. Again from what I've read, an XSLT 2.0 processor would give me more options.

In the end I don't really care what solution I use...assuming it doesn't significantly hit memory/time. I have complete control over the development so to answer your question I have complete flexibility! Simply put, I need to render a specific set of nodes in a data XML file and tell the XSLT file which nodes they are!


Thanks,

Bernard.
 
Hi Bernard,

I will create an example based upon the assumption that you can place your 'selection list' in an XML document, and show you how to use document() to pull stuff from your actual data document. Expect something tomorrow.

My current work involves replacing a huge collection of hard-coded PCL reports with XML pipeline, using Apache FOP. One of the biggest issues is discerning the actual data elements among all the noise surrounding the creation of a PCL document. Once I assemble the data elements it is rather easy to serialize and produce the report. So - this is all to say I am spending a lot of time in creation of XSL that feeds Apache FOP.

(By the way, you can 'preprocess' your XML using any XXSL process you want, to create XSL-FO outside the scope of Apache FOP. At least on the command line, there is a way to introduce the XSL-FO.)

Tom Morrison
Hill Country Software
 
I do have another idea which works using tokens.

In Java, I determine the items which I want rendered/processed. I concatenate those item names into one string, delimited by a token, say '|'. If the '|' is used in any item name, I'll have to use a '||' and so on until I find a safe token. I also add '|' to the front and back, ending up with say '|apple|orange|'. I pass this to the transformer using setParameter.

In the stylesheet, when I can then use something like apply-templates or for-each and in the select only match item names (with a '|' added either side) with the large concatenated string (thanks Frank for the suggestion!).

I'll give this a go and see what happens...
 
That will work - I have used the technique myself. I would be somewhat concerned about how well it scales if you are processing large documents in a heavy production environment, but worry about that after you get it working.

Tom Morrison
Hill Country Software
 
I got sidetracked from looking at a string concat/token solution but managed to figure out how to pass a Java object into the XSLT and access it. Here it is hoping it'll help someone in the future...

In Java, create your Java object and set it as a parameter into the javax.xml.transform.Transformer:

ArrayList<String> list = new ArrayList<String>();
list.add( "apple" );
list.add( "orange" );
transformer.setParameter( "list-of-items-to-print", list );

In the XSLT...

The header needs to know about the Java object: <xsl:stylesheet version="1.1" xmlns:xsl=" xmlns:fo=" exclude-result-prefixes="fo arrayList" xmlns:arrayList="
and you need a parameter definition for the incoming ArrayList: <xsl:param name="list-of-items-to-print" value="" />

In the guts of XSLT, I had test code to ensure the data was coming in...

<fo:block>
list-of-items-to-print: <xsl:value-of select="$list-of-strings" />
list-of-items-to-print.size(): <xsl:value-of select="arrayList:size( $list-of-items-to-print )" />
list-of-items-to-print[ 0 ]: <xsl:value-of select="arrayList:get( $list-of-items-to-print, 0 )" />
list-of-items-to-print[ 1 ]: <xsl:value-of select="arrayList:get( $list-of-items-to-print, 1 )" />
</fo:block>

To actually use this list of items, instead of iterating over it, I used the contains() method to check if the current item (given by XLST iterating over items) is present in the list...

<xsl:template match="items">
<xsl:apply-templates select="./item" />
</xsl:template>

<xsl:template match="item">
<xsl:if test="arrayList:contains( $list-of-items-to-print, string( item ) )">
<fo:block>Found '<xsl:value-of select="item"/>'</fo:block>
</xsl:if>
</xsl:template>

Note that all the above Java/XSLT code is transcribed from a larger chunk of real code and so it may not compile...but should be close to the real thing.


Cheers,

Bernard.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top