Getting The XML Data Inside Custom XPath function - java

Is there a way to get the current xml data when we make our own custom XPath function (see here).
I know you have access to an XPathContext but is this enough?
Example:
Our XML:
<foo>
<bar>smang</bar>
<fizz>buzz</fizz>
</foo>
Our XSL:
<xsl:template match="/">
<xsl:value-of select="ourFunction()" />
</xsl:template>
How do we get the entire XML tree?
Edit: To clarify: I'm creating a custom function that ends up executing static Java code (it's a Saxon feature). So, in this Java code, I wish to be able to get elements from the XML tree, such as bar and fizz, and their CDATA, such as smang and buzz.

Try changing your XSL so you call 'ourFunction(/)'. That should pass the root node to the function. You could also try . or ..
You'll presumably need to change the signature of the implementing function, I'll let someone else help with that.

What about select the current node selecting the relevant data from the current node into an XSL parameter, and passing that parameter to the function? Like:
<xsl:value-of select="ourFunction($data)" />

Related

show XML in HTML with inline stylesheet

It's not an duplicate, the problem ist not show an xml in html, rather to show xml with occasional stylesheets for single lines.
i'm looking since hourse for an solution to show xml in a html page with inline css.
Background: Input data is an xml file, what will be compared with another xml file in java. The comparison is processed by org.custommonkey.xmlunit.
Now i like to transform the xml via xslt and the xpathes from the comparison result of xunit.
Input XML (Multilevel, but for this example very simplified, the Field changed by is exemplary for all other fields)
<ROOT>
<MATDETAIL>
<OUTPUT>
<GENERAL>
<CREATED_ON/>
<CREATED_BY>ORIGINAL USER</CREATED_BY>
<LAST_CHNGE/>
<CHANGED_BY>NEW USER</CHANGED_BY>
</GENERAL>
<RETURN>
<TYPE>S</TYPE>
<MESSAGE/>
<LOG_NO/>
<LOG_MSG_NO>000000</LOG_MSG_NO>
</RETURN>
</OUTPUT>
</MATDETAIL>
</ROOT>
XUnit Diff Result XPath
/ROOT[1]/MATDETAIL[1]/OUTPUT[1]/GENERAL[1]/CHANGED_BY[1]/text()[1]
now i will be perfom an translation via xslt (already existing)
my xsl shows like that
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" mode="unescape"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/ROOT[1]/MATDETAIL[1]/OUTPUT[1]/GENERAL[1]/CHANGED_BY[1]">
<xsl:element name = "span">
<xsl:attribute name="style">font-weight:bold; color:red </xsl:attribute>
<xsl:copy>
<xsl:value-of select = "current()" />
</xsl:copy>
<xsl:text><== Expected: dasda</xsl:text>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
that will give me this result
<ROOT>
<MATDETAIL>
<OUTPUT>
<GENERAL>
<CREATED_ON/>
<CREATED_BY>ORIGINAL USER</CREATED_BY>
<LAST_CHNGE/>
<span style="font-weight:bold; color:red "><CHANGED_BY>NEW USER</CHANGED_BY><== Expected: ORIGINAL USER</span>
</GENERAL>
<RETURN>
<TYPE>S</TYPE>
<MESSAGE/>
<LOG_NO/>
<LOG_MSG_NO>000000</LOG_MSG_NO>
</RETURN>
</OUTPUT>
</MATDETAIL>
</ROOT>
But i'm not able to show this xml in html correctly (with stylesheets).
Either i get no tags, so to see the xml strcuture is very complicated
also i get the xml structure but without stylesheets.
I like to show the result, from comparison with xunit of the two files, in a webservice and this also colored, so you can easy see which elements have change or are incorrectly.
The Problem is to show the full xml + and that seems to be the real problem to color the entries with stylesheets. I can show the xml, but without inline stylesheets, or i can show the styled text but not both.
I already tried to replace lt and gt inside xslt but failed.
I tried to replace lt and gt in the rseult via example java. so every lt and gt are html coded tags. but the result shows very ugly. My colleague has meant that we can not use it in any way.
I tried to style the xml via css > definiton per tag.
But the the structure and fields are full dynamicly, so i cant define css for every tag or field. On the other hand every field can be correct or wrong, depends also on the xml file like to compare. So the field changedby is at the first comparison correct, but on an other comparison its wrong
Unfortunately, alternative implementations are not an option. I have to do this somehow with the means available to me. (Java, XML & XSL, JS, HTML, CSS).
I hope to get good ideas to solute this.
I would like to thank you in advance.
You could use XSLT to transform the xml to HTML so you can display it in a browser. Or build a JS clientside that parses the XML from the server and displays it. This would be something like angularjs.
I'm not sure to understand your request.
If you want to display XML in HTML, you can insert it in the PRE tags : <pre><ROOT>...</ROOT></pre>
If you want to display XML as it could be displayed un an HTML/XML editor you can use Ace : https://ace.c9.io/#nav=about
Other option but not tried : https://highlightjs.org

localizing xsl for different languages

I have some static xsl to translate dynamic xml into html to response to browser. The rest of the web pages use Spring MVC for view. So they can be localized by using Spring's messages.properties file written in my language. But I don't know how to localize the text nodes in the static xsl using the same method. More specific below.
In Spring's web page, I can
<title><spring:message code="title.MyTitle"/></title>
In my static xsl, I have
<xsl:stylesheet ........
<xsl:output method="html"/>
<xsl:template match="/">
.....
<title>My Title</title>
and I want something like this
<xsl:stylesheet ........
<xsl:output method="html"/>
<xsl:template match="/">
.....
<title><spring:message code="title.MyTitle"/></title>
Of course the above doesn't work. But I hope I can keep all titles and labels in messages.properties for easy changes between languages. How can I do this? Please help.
Jirka Kosek has a technique for doing l10n lookups at http://www.xml.com/pub/a/2003/11/05/xslt.html. I thought he'd made a whole system for doing l10n with XSLT, but I can't find that now.
Separately, if your properties files are text rather than the XML property file format that Java also understands, the general technique would be:
Use unparsed-text() to get the text of the properties file
Tokenize on line-ends (those not preceded by /, that is)
Make a variable that contains an element for each of those strings, where the key is in an attribute value and the text is the text content of the element
All that's done so far is mimic the XML property file format.
Make a key that matches on the elements and uses the attribute value as the lookup
Make a function that takes the string as a parameter and does a lookup on the key, using the string as the second parameter and the variable as the third

keep only wanted elements in xml with the given list of xpath

I have some complex xml structure. Some times i want to keep only the elements match my runtime list of xpath entries.
Sample xml
<Employee>
<Address>
<addressLine1>Dummy Line 1</addressLine1>
<zip>535270</zip>
</Address>
<Department>
<id>102</id>
<name>development</name>
</Department
</Employee>
Sample xpath entries can be some time like
//Employee/Address
//Employee/Department/
//Employee/Department/name
In the above xpath if you observe we have Department and Name inside department then in that case I can ignore Department.Also above xpath entries can be as below too
//Employee/Address
//Employee/Department/name
Resultant xml i want as below
<Employee>
<Address>
<addressLine1>Dummy Line 1</addressLine1>
<zip>535270</zip>
</Address>
<Department>
<name>development</name>
</Department
</Employee>
I realized I can achieve this through xslt. So I want the xslt for this kind of generic requirement. Also my current code is in java. Is there any better alternative in java??
I have to admit that I do not fully understand your requirements, but what I do see is that:
You have a set of XPath
You seem to want to get the union of these XPath statements if applied to your input document
Duplicates removed (just like XPath union expression)
Some magic about certain elements that may appear even if they are not in the XPath statement list.
My original reflex was: use xsl:evaluate, but considering you are rooting all your XPath expressions, this may not give you the results you want. Also, it requires an XSLT 3.0 processor.
With XSLT 2.0, you could do something like this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:strip-space elements="*" />
<xsl:output indent="yes" />
<xsl:variable name="patterns" as="xs:string*">
<xsl:sequence select="(
'foo/bar',
'foo/test',
'foo/bar/zed')" />
</xsl:variable>
<xsl:template match="node()[true() = (
for $p in $patterns
return ends-with(
string-join(current()/ancestor-or-self::*/name(),
'/'), $p))]">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
It is just meant to get you started, it is not meant as a full-fledged solution. It matches XPath of the production //QName/QName, like the one in your example. I removed the trailing //, and simply match when the current path matches any of the paths (taking into account an implicit descendent-or-self, as in your example).
You probably want to wrap the for-expression in a function and call the function to map the concatenation of the current path to any of your paths in your list.
In its current form, you will also need to supply the paths leading up to a deeper path, or you will have to implement an fn:snapshot-like function to copy the ancestor nodes as well.
Anyway, I think this is a simple enough approach to mimic not necessarily xsl:evaluate, but mimic pattern matching based on a path, as your question seems to imply.

Validate XML against XSD and enrich with validation results

I'm building a java tool to validate an xml document and build an html report containing the input data and validation results.
I think that a possible way is:
validate XML with XSD
enrich the XML with validation results
transform the enriched XML in the final HTML report (this point is not object of the question)
First and foremost, is this a valid approach? Or there are more suitable ways to get those things done in java?
If this is is a viable solution, how can i implement step 2?
For example, if I start from this input document:
<parent>
<child id="a correct id" type="a correct type"/>
<child id="an incorrect id" type="an incorrect type"/>
</parent>
How can I produce an enriched output document like that:
<parent>
<child id="a correct id" type="a correct type">
<results>
<result>id is correct</result>
<result>type is correct</result>
</results>
</child>
<child id="an incorrect id" type="an incorrect type">
<results>
<result>id is NOT correct</result>
<result>type is NOT correct</result>
</results>
</child>
</parent>
First, there are many ways of going about this. There are other tools like schematron that provide languages for describing validation results, and the ability to transform the results of validation into pretty HTML. There are numerous java packages that actually do schema validation, so most of what you're trying to accomplish should be "glue code". Make sure you don't attempt to do schema validation in your java code.
So next, I'm not sure what your requirements are for wanting to transform the original XML file after validation. Usually you'd dump a validation result set as a separate file. Does the schema for the original XML permit your additions that you're putting in?
In general, if you wanted to transform the original input, you could go about this by writing an XSLT program that takes the validation results file, and the original source file, and then transforms the original file using those validation results. But I don't recommend that because I think your situation might call for a different design that doesn't transform the original file, unless you have more requirements you want to go into more depth about.
Another option would be straightforward DOM manipulation. After validation, you could load the DOM for the input document, manipulate it, then write it back to the same original file.
But seriously -- before you adopt any approach for step 2, make sure that your requirements really call for it.
One approach worth exploring: Xerces-J provides access to the post-schema-validation infoset (PSVI), and can in fact serialize it as XML. For small documents, at least, you may find that XML representation of the PSVI suffices for your purposes.
The PSVI representation made available by Xerces-J (and by xsv) is not, it should be said, anything like an annotated copy of the input. But it can be transformed into a form like the one you show using normal XML processing.
I'm returning to this question after I got some deeper understanding and experience of XSD and XSLT and eventually built my project on that knowledge.
My original question had some misleading points.
My objective was to process an XML with the ONLY objective to produce a corresponding HTML report, containing the XML data in a readable form along with the "validation" results against a set of business rules.
My wrong assumption was that I should necessarily have to validate the XML against an XSD, but that brought me some significant challenges in development:
XSD 1.0 did not fit completely my business rules. So I had to switch to XSD 1.1
then I had to set up an XSD 1.1 compliant validator in java (Xerces2J)
and finally I started thinking on how to build a fine-grained validator based on those premises.
It was at that time that I realized that this process was a little overkill: what I really needed was just a TRANSFORMATION from XML to HTML: the fine-grained validation could have been done inside the transformation process, all together with an XSLT.
To answer my own question in the most generic form:
I would still use XSD for a very basic preliminary validation, and then use XSLT to check for more complex validation rules and enrich the XML.
This is the XSLT to transform the source xml of my own question into the result (still XML).
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="parent">
<xsl:for-each select="child">
<xsl:call-template name="processChildren"/>
</xsl:for-each>
</xsl:template>
<!-- this template processes the children nodes, applying a sample test clause -->
<xsl:template name="processChildren">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
<results>
<xsl:choose>
<xsl:when test="contains(#id,'incorrect')">
<result>id is NOT correct</result>
</xsl:when>
<xsl:otherwise>
<result>id is correct</result>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="contains(#type,'incorrect')">
<result>type is NOT correct</result>
</xsl:when>
<xsl:otherwise>
<result>type is correct</result>
</xsl:otherwise>
</xsl:choose>
</results>
</xsl:copy>
</xsl:template>
<!-- this template copy the contents of the node unaltered -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Trying to print out node values in XSLT using variable elements names

So here's a problem that's been bugging me for the last few days. It should be fairly easy, but XSLT is just such a pain to debug. We're using Xalan 1.0 on java 1.6
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<rfb2>
<rfb2_item>
<VALDATE>2011-10-23</VALDATE>
<FUND_ID>300</FUND_ID>
<SEC_ID>34567</SEC_ID>
</rfb2_item>
<rfb2_item>
<VALDATE>2011-1-09</VALDATE>
<FUND_ID>700</FUND_ID>
<SEC_ID>13587</SEC_ID>
</rfb2_item>
<rfb2_item>
<VALDATE>2011-3-09</VALDATE>
<FUND_ID>200</FUND_ID>
<SEC_ID>999334</SEC_ID>
</rfb2_item>
<rfb2>
We need to transform the XML into a comma-separated list of values for each rfb2_item, so the style sheet always iterates the rfb2_item nodes. We are using a parameter in the style sheet to control which elements of rfb2_item (valdate,fund_id,sec_id) that will be output, and in what order, for example
<xsl:param name="$outputElements" select="'VALDATE,FUND_ID'"/>
..outputs...
2011-10-23,300
2011-1-09,700
2011-3-09,200
<xsl:param name="$outputElements" select="'SEC_ID'"/>
..outputs...
34567
13587
999334
Special case where if $outputElements is '*', just output the elements in the order they appear in the input xml
<xsl:param name="$outputElements" select="'*'"/>
..outputs...
2011-10-23,300,34567
2011-1-09,700,13587
2011-3-09,200,999334
So, my question is how do we write a template to create the desired output based on the $outputElements parameter? A working example would be great...
Yup, FailedDev is right. Someone would write it for you:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:param name="outputElements" select=" 'FUND_ID,SEC_ID,VALDATE' " />
<xsl:template match="rfb2_item">
<xsl:for-each select="*[contains($outputElements, local-name()) or $outputElements = '*']">
<xsl:sort select="string-length(substring-before($outputElements, local-name(.)))" />
<xsl:value-of select="text()" />
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Bit of explanation. The xsl:for-each is gonna select each element in the current rfb2_item for which the local name is contained in the outputElements parameter, or for which the outputElements parameter is * (which would always yield true if that's the case). It's then gonna sort those based on the length of the substring that goes before that local name in outputElements. Since this value becomes higher when the name occurs later in that parameter, this results in ordering based on your parameter.
Example: element VALDATE would yield FUND_ID,SEC_ID for the substring-before function, which in turn would yield 14 as string length. This is higher than the 8 that you'd get for SEC_ID, meaning the VALDATE value is ordered after SEC_ID.
After the xsl:sort, we're simply using xsl:value-of to output the element value. You might want to trim extraneous whitespace there. Finally, we're testing if the position is not equal to that of the last node in the current context (which is that of xsl:for-each after sorting) and if so, output a comma. This avoids outputting a comma after the last value.
The line break I've inserted using xsl:text assumes the Windows/DOS convention. Remove the 
 if the file should only use new line characters for line breaks, instead of carriage return + new line.
Note that this does not escape commas in your CSV output! I'll leave that up to you. It could be interesting to look into using extension functions for delegating this task to Java if it proves too difficult in XSLT/XPath.
Sometimes in this kind of situation it's worth looking at the possibility of generating or modifying XSLT code using XSLT. You can take the parameterization a lot further that way - for example controlling which fields are output, how they are sorted, whether they are grouped, selection criteria for which rows are selected, etc etc.

Categories