xml to csv through xslt transformation - java

I know there are a lot of questions on this, but I am stuck on the problem of converting xml to csv using xslt. I used an example xslt for testing:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="delimiter" select="','"/>
<xsl:key name="field" match="sObject/*" use="name()"/>
<xsl:template match="/">
<xsl:for-each select="/*/*/*[generate-id()=generate-id(key('field', name())[1])]">
<xsl:value-of select="name()"/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="/*/sObject">
<xsl:variable name="property" select="." />
<xsl:for-each select="$property/*">
<xsl:variable name="value" select="." />
<xsl:value-of select="$value"/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
<xsl:if test="position() = last()">
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This code works well on this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<sObjects>
<sObject>
<Name>Raagu</Name>
<BillingStreet>Hoskote</BillingStreet>
</sObject>
<sObject>
<Name>Rajath</Name>
<BillingStreet>BTM</BillingStreet>
<age>25</age>
</sObject>
<sObject>
<Name>Sarath</Name>
<BillingStreet>Murgesh</BillingStreet>
<location>Bangalore</location>
</sObject>
</sObjects>
The example I found on SO, but could anyone give me a hint on how to process an xml, which has more depth in it? Say:
<?xml version="1.0" encoding="ISO-8859-1"?>
<sObjects>
<sObject>
<Person>
<Name>Raagu</Name>
<BillingStreet>Hoskote</BillingStreet>
</Person>
<Country>Germany</Country>
</sObject>
<sObjects>
I will be very grateful for any hint on this. Thanks in advance.

If you can assuming the left nodes always correspond to fields in your CSV output, you should consider redefining the field key like-so
<xsl:key name="field" match="*[not(*)]" use="local-name()"/>
(I am using "local-name()" here so that it can cope with XML that involves namespaces)
Then, define a variable to get the distinct field names
<xsl:variable
name="fields"
select="//*[not(*)][generate-id()=generate-id(key('field', local-name())[1])]" />
Here //* will look for elements at any depth in the XML. You can then easily iterate over this variable to output the header row as currently. So far so good.
However, despite you saying the current XSLT is working well, it isn't. Because of this line:
<xsl:for-each select="$property/*">
This is assuming each sObject has the same number of child elements (i.e. it has all fields present, and in the right order), which in your XML sample is not the case.
Firstly, I would redefine the property variable to be all 'leaf' elements under the current element, at any depth.
<xsl:variable name="properties" select=".//*[not(*)]" />
Then, you need to iterate over the fields variable, not the properties element
<xsl:for-each select="$fields">
The properties element is then used to find the element within it that matches the current field name, like so
<xsl:value-of select="$properties[local-name()=local-name(current())]"/>
Try the following XSLT then
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="delimiter" select="','"/>
<xsl:key name="field" match="*[not(*)]" use="local-name()"/>
<xsl:variable name="fields" select="//*[not(*)][generate-id()=generate-id(key('field', local-name())[1])]" />
<xsl:template match="/">
<xsl:for-each select="$fields">
<xsl:value-of select="local-name()"/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="/*/*">
<xsl:variable name="properties" select=".//*[not(*)]" />
<xsl:for-each select="$fields">
<xsl:value-of select="$properties[local-name()=local-name(current())]"/>
<xsl:if test="position() != last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
<xsl:if test="position() = last()">
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This should work on both your XML samples. Also note there is not a mention of sObject in sight (The assumption being the children of the root element correspond to the records, and the leaf elements correspond to fields)
Do note, the XSLT uses a technique called Muenchian Grouping to determine the distinct field names. Read about this, and learn it well.

Related

How to convert complex Xml to csv?

I am writing a program in java( pre-junior), I really need help with xslt transformation. It is necessary to make a csv file from xml.
I got this xslt filter:
<?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" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="node()" name="conv">
<xsl:call-template name="loop"/>
</xsl:template>
<xsl:template name="loop">
<xsl:for-each select="./*[count(*) = 0]">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:if test="position() = last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="./*[(count(*) != 0) and (name()!='PARAMETRS')] ">
<xsl:call-template name="loop"/>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Source xml:
<Integration>
<PARAMETRS>
<ID>AZD</ID>
<DATE>2020-01-01</DATE>
</PARAMETRS>
<ORG>
<Thing>
<object>10220</object>
<type>U</type>
<dyn>
<items>
<val>988009</val>
<datebegin>2019-12-12</datebegin>
</items>
</dyn>
</Thing>
<Thing>
<object>10221</object>
<type>U</type>
<dyn>
<items>
<val>988010</val>
<datebegin>2019-12-13</datebegin>
</items>
<items>
<val>988011</val>
<datebegin>2019-12-14</datebegin>
</items>
</dyn>
</Thing>
</ORG>
</Integration>
In the output, I get comma-separated lines, and a few more lines (those same items) with the values below. and can't figure out how to concatenate the values ...
I would do it via value-of select = "concat" but my may have several dyn (1, 2, 3 ...), hence this is not suitable.
The output needs a csv separated by commas.
Please advise how to concatenate the item with its parent? Or there are simpler ways to parse xml with a different number of subsections(childs).
Expected output:
10220,U,988009,2019-12-12
10221,U,988010,2019-12-13,988011,2019-12-14
The output you show can be easily obtained using the following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/Integration">
<xsl:for-each select="ORG/Thing">
<xsl:value-of select="object"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="type"/>
<xsl:text>,</xsl:text>
<xsl:for-each select="dyn/items">
<xsl:value-of select="val"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="datebegin"/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note that the output has a set of columns for each items; this is not an ideal CSV structure.
If you can use XSLT 2.0, it opens up new powerful functionality.
Oracle XML Developer Kit (XDK) supports XSLT 2.0
Here is the link: Using the XSLT Processor for Java
The approach below is doing the following:
Using string-join() function to concatenate all child elements values
on a different hierarchy level via .//*/(text()[1] expression.
xs:token casting removes white spaces.
XPath predicate [. != ''] removes empty sequence members.
XSLT 2.0
<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/Integration">
<xsl:for-each select="ORG/Thing">
<xsl:value-of select="string-join((.//*/(text()[1] cast as xs:token?))[. != ''],',')"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output
10220,U,988009,2019-12-12
10221,U,988010,2019-12-13,988011,2019-12-14
Based on the Marting Honnen great tip, here is even more concise XSLT 2.0 version without any loop.
XSLT 2.0
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/Integration">
<xsl:value-of select="ORG/Thing/string-join((.//*/(text()[1] cast as xs:token?))[. != ''],',')" separator="
"/>
</xsl:template>
</xsl:stylesheet>

Illegal value used for attribute name during xslt Transformation in java

Hi I'm trying to transform an XML to another XML using xsl. The XSL transformation is working fine when tested in Oxygen developer and in VM also.
But still I'm getting an Warning like "Illegal value used for attribute name" at line number 20 and 23.
XSL which i used is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cl="http://tyryt.com/abc/abc_order"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:java="http://xml.apache.org/xslt/java"
exclude-result-prefixes="cl xsd java" version="1.0">
<xsl:output indent="yes" method="xml" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:template name="generateInsightsHeader">
<xsl:for-each select="/">
<xsl:element name="pon">
<xsl:value-of select="*/cl:HDR/cl:PON"/>
</xsl:element>
<xsl:element name="version">
<xsl:value-of select="*/cl:HDR/cl:VER"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template name="copyWholeXml" match="*">
<xsl:if test="local-name() != 'brmserrorxml'">
<xsl:element name="{local-name()}">
<xsl:for-each select="#*">
<xsl:if test="../#protocoltype != ''">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<IntermediateXml xmlns:json="http://json.org/">
<xsl:choose>
<xsl:when test="cl:SAMPLE_ORDER">
<xsl:call-template name="generateInsightsHeader"/>
<xsl:call-template name="copyWholeXml"/>
</xsl:when>
</xsl:choose>
</IntermediateXml>
</xsl:template>
</xsl:stylesheet>
and Sample XML i used is:
<?xml version="1.0"?>
<SAMPLE_ORDER class="1" index="0" xmlns="http://tyryt.com/abc/abc_order" service="SAMPLE_ORDER">
<header>
<interfaceid>INTERFACE_REQUIRED</interfaceid>
<actionrequired>Transmit</actionrequired>
</header>
<HDR>
<MESSAGE_ID>229364ASE</MESSAGE_ID>
<DATE>2017-09-04T12:29:11+05:30</DATE>
<PON>229364ASE10</PON>
<VER>01</VER>
</HDR>
<LOCAL_MESSAGE>
<LOCAL_ADMIN>
<LOCQTY>111</LOCQTY>
<AN>JHGKJ679868</AN>
<CC>005F</CC>
<AUTHORIZATION>
<DDD>20171228</DDD>
<ACTL>01234567899</ACTL>
</AUTHORIZATION>
</LOCAL_ADMIN>
<LSR_BILL>
<ACNA>WEE</ACNA>
</LSR_BILL>
<CONTACT>
<INIT>ASDDFAD</INIT>
<INIT_TEL_NO>2543654576</INIT_TEL_NO>
<INIT_EMAIL>abc#gmail.com</INIT_EMAIL>
</CONTACT>
</LOCAL_MESSAGE>
<LS>
<LS_SVC_DET>
<SVC_DET_GRP>
<LOCNUM>145</LOCNUM>
<LNUM>78945</LNUM>
<LNA>D</LNA>
<ECCKT>98698KJGKUJGUKJH</ECCKT>
</SVC_DET_GRP>
</LS_SVC_DET>
</LS>
</SAMPLE_ORDER>
For Transformation i'm using Xalan jar.
Please help me to solve this problem.
Thanks in Advance
The root node has no name, or more precisely, the name of the document's root node is the empty string ''. So when you call your template "copyWholeXml" and the current context node is the root node, local-name() evaluates to '', and <xsl:element name="{local-name()}"> is invalid, because you cannot generate a new XML element with an empty string as the name.
When you call your template "copyWholeXml" make sure that you are not entering the named template with the document root as the context node

XSLT format-date for an attribute

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd created_at="2016-12-15T15:02:55Z">
<title created_at="2016-12-15T15:02:55Z">Empire Burlesque</title>
<artist created_at="2016-12-15T15:02:55Z">Bob Dylan</artist>
<cover created_at="2016-12-15T15:02:55Z"/>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
I want to format all occurrences of created_at attribute
input format YYYY-MM-DDTHH:MM:SSZ
output format YYYY-MM-DD HH:MM:SS
I am currently using this following xslt
<?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"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!-- Edit dates to conform to dbunit format-->
<xsl:template match="#created_at">
<xsl:copy>
<xsl:call-template name="formatdate">
<xsl:with-param name="datestr" select="#created_at"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="formatdate">
<xsl:param name="datestr" />
<!-- input format YYYY-MM-DDTHH:MM:SSZ -->
<!-- output format YYYY-MM-DD HH:MM:SS -->
<xsl:variable name="datetext">
<xsl:value-of select="substring-before($datestr,'T')" />
</xsl:variable>
<xsl:variable name="timetext">
<xsl:value-of select="substring($datestr,12,18)" />
</xsl:variable>
<xsl:value-of select="concat($datetext, ' ', $timetext)" />
</xsl:template>
</xsl:stylesheet>
However as I debug through the transformation xslt it does not seem to enter the formatdate call-template. Is my xpath wrong? I found articles on modifying the node, but not the attribute. Any help would be much appreciated.
Thank you
Why not simply:
<xsl:template match="#created_at">
<xsl:attribute name="created_at">
<xsl:value-of select="substring(translate(., 'T', ' '), 1, 19)" />
</xsl:attribute>
</xsl:template>
Note: you cannot use xsl:copy if you want to change an attribute's value.
From your post, it sounds like all you need is simple string processing.
Why your code isn't working the way you want
You're handling the #created_at attributes with this template:
<xsl:template match="#created_at">
<xsl:copy>
<xsl:call-template name="formatdate">
<xsl:with-param name="datestr" select="#created_at"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
The kicker here is that you're using <xsl:copy>. When used with attributes, <xsl:copy> copies the entire attribute, name and value both. And since attributes can't contain any children, the children of your <xsl:copy> instruction are ignored -- so the XSLT processor never evaluates the <xsl:call-template name="formatdate"> instruction.
A different approach that works
Instead of using <xsl:copy>, you need to instead use <xsl:attribute> to create an attribute in a way where you can also specify the value. In this case, you already know the name of the attribute you want to create, so you could hard-code the name value as created_at. For a more flexible approach, you could instead give the name value as {name(.)} -- this just grabs the name of the attribute being processed, which is closer in behavior to what you probably thought <xsl:copy> would do. :)
It is also possible to produce the desired string in a single xsl:value-of expression, without relying on so many variables.
<xsl:template match="#created_at">
<xsl:attribute name="{name(.)}">
<xsl:value-of select="concat(substring-before(., 'T'), ' ', substring-before(substring-after(., 'T'), 'Z'))"/>
</xsl:attribute>
</xsl:template>
Breaking down that select statement:
Use concat() to stitch together multiple bits of string.
Use substring-before(., 'T') to grab everything before the T -- that's the date portion.
' ' adds the single space in the middle.
substring-before(substring-after(., 'T'), 'Z') --
The inner expression substring-after(., 'T') grabs everything after the T -- that's the time portion.
However, there's that pesky Z on the end, so we use substring-before as the outer expression to lop that off.
No need for variables, and it gets the job done. Confirmed to work with XSLT 1.0.
Try this
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='xml' indent='yes'/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!-- Edit dates to conform to dbunit format-->
<xsl:template match="#created_at">
<xsl:call-template name="formatdate">
<xsl:with-param name="datestr" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="formatdate">
<xsl:param name="datestr" />
<!-- input format YYYY-MM-DDTHH:MM:SSZ -->
<!-- output format YYYY-MM-DD HH:MM:SS -->
<xsl:variable name="datetext">
<xsl:value-of select="substring-before($datestr,'T')" />
</xsl:variable>
<xsl:variable name="timetext">
<xsl:value-of select="substring($datestr,12,8)" />
</xsl:variable>
<xsl:attribute name="created_at">
<xsl:value-of select="concat($datetext, ' ', $timetext)" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>

Unable to embed delimiter when converting XML to CSV using XSLT using Java

I am trying to convert XML to CSV using XSLT. I have seen many similar posts on this site, but wanted to mention specific requirement and the issue I am seeing in my code.
This is my well formed XML file content. Please note, I am getting this XML from the database as a string, so it is not formatted.
XML file
<myxml ver="1.0"><name>smile</name><email>test#gmail.com</email><phone_number/><job_title>Engineer</job_title><company_name>Stack Overflow</company_name><submission-datestamp>3/14/11 8:29 PM</submission-datestamp></myxml>
Here is the XSLT file
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="myxml">
<xsl:for-each select="*" >
<xsl:if test="position() != last()">
<xsl:value-of select="normalize-space(.)"/>,
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I am getting below CSV format content:
smile,
test#gmail.com,
,
Engineer,
Stack Overflow,
3/14/11 8:29 PM
Requirement is:
I need to display the header first
Comma separated list as in
smiletest#gmail.com,EngineerStack Overflow,3/14/11 8:29 PM
Can you please help me fix my code? The problem is in XSLT file.
You are getting the extra carriage returns because you have mixed-content inside of the template. The stylesheet assumes that the whitespace following the comma is significant. You can prevent that by putting your text inside of <xsl:text>. That tells the stylesheet which text is explicitly part of the output, so if there is nothing else left but whitespace, it is assumed to be insignificant.
I reworked the stylesheet slightly to be more of a "push style" stylesheet using xsl:apply-templates instead of xsl:for-each, and also added carriage-return at the end of each line:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates mode="header"/>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="myxml/*" mode="header">
<xsl:value-of select="local-name()"/>
<xsl:choose>
<xsl:when test="position() = last()">
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>,</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="myxml/*">
<xsl:value-of select="normalize-space(.)"/>
<xsl:choose>
<xsl:when test="position() = last()">
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>,</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Produces the following output:
name,email,phone_number,job_title,company_name,submission-datestamp
smile,test#gmail.com,,Engineer,Stack Overflow,3/14/11 8:29 PM

transform int values to color objects using XSL and Java

I am new in XSL and I have an xml file with tags representing int rgb colors,
I want using XSL to convert them into Color object tags:
my xml contains these tags
<?xml version="1.0" encoding="UTF-8"?>
.
.
.
<ForeGroundColour>-16776961</ForeGroundColour>
.
.
.
</xml>
the desired xml is:
<ForeGroundColour>
<red>102</red>
<blue>102</blue>
<green>255</green>
<alpha>255</alpha>
</ForeGroundColour>
my XSL file is:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:Color="java.awt.Color"
xmlns:Integer="java.lang.Integer" exclude-result-prefixes="Color Integer ">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<!-- identity template !-->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="//*[contains(name(),'ForegroundColour')]">
<xsl:copy>
<xsl:variable name="rgb" select="." />
<xsl:variable name="color" select="Color:new(Integer:parseInt($rgb))" />
<xsl:element name="red">
<xsl:value-of select="Color:getRed($color)" />
</xsl:element>
<xsl:element name="blue">
<xsl:value-of select="Color:getBlue($color)" />
</xsl:element>
<xsl:element name="green">
<xsl:value-of select="Color:getGreen($color)" />
</xsl:element>
<xsl:element name="alpha">
<xsl:value-of select="Color:getAlpha($color)" />
</xsl:element>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I have two problems:
1- when I run this code, I get the following exception:
Exception in thread "main" java.lang.VerifyError: (class: transformer, method: template$dot$1 signature: (Lcom/sun/org/apache/xalan/internal/xsltc/DOM;Lcom/sun/org/apache/xml/internal/dtm/DTMAxisIterator;Lcom/sun/org/apache/xml/internal/serializer/SerializationHandler;I)V) Expecting to find double on stack
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2389)
at java.lang.Class.getConstructor0(Class.java:2699)
at java.lang.Class.newInstance0(Class.java:326)
at java.lang.Class.newInstance(Class.java:308)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:364)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:394)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl.newTransformer(TransformerFactoryImpl.java:649)
at com.nyfix.ostp.server.workspacemigrator.XMLTransformer.transform(XMLTransformer.java:44)
at com.nyfix.ostp.server.workspacemigrator.XSLMigrator.migrate(XSLMigrator.java:45)
at com.nyfix.ostp.server.workspacemigrator.WorkspaceMigratorFactory.<init>(WorkspaceMigratorFactory.java:19)
at com.nyfix.ostp.server.workspacemigrator.WorkspaceMigratorFactory.main(WorkspaceMigratorFactory.java:45)
2- when I use the template just to test the inserted nodes,
<xsl:template match="//*[contains(name(),'ForegroundColour')]">
<xsl:copy>
<xsl:variable name="rgb" select="." />
<xsl:element name="red">
<xsl:value-of select="$rgb" />
</xsl:element>
<xsl:element name="blue">
<xsl:value-of select="$rgb" />
</xsl:element>
<xsl:element name="green">
<xsl:value-of select="$rgb" />
</xsl:element>
<xsl:element name="alpha">
<xsl:value-of select="$rgb" />
</xsl:element>
</xsl:copy>
</xsl:template>
the xml is transformed without indentation:
<ForegroundColour>
<red>-16776961</red>
<blue>-16776961</blue>
<green>-16776961</green>
<alpha>-16776961</alpha>
</ForegroundColour>
can anyone help me with this? thanks in advance
XSLT variables can contain only strings, booleans, numbers or node-sets - the Color:new(...) call returns a Java object, that cannot be assigned to an XSLT variable, hence the exception.
Possible solutions:
1) Create your own extension class with methods stringToRed, stringToBlue etc defined so that they accept a string, convert it internally to the Color object and then extract the wanted component (Red, Blue etc) as a number or string . These functions then can be used directly:
<xsl:element name="red">
<xsl:value-of select="MyColorFunctions:stringToRed(.)" />
</xsl:element>
2) 'Reverse-engineer' the algorithm that converts a color expressed as an integer to its component, and implement it directly in XSLT, without using extension function. Typically the red component will be the 8 least significative bits, the blu the next 8 bits and so on (or vice-versa), and you have to handle the negative values (convert from 2-complement to absolute) - so something like this:
<xsl:variable name="color">
<xsl:choose>
<xsl:when test="number(.) >=0">
<xsl:value-of select="number(.)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="4278190080+number(.)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<red>
<xsl:value-of select="floor($color mod 256)" />
</red>
<green>
<xsl:value-of select="floor(($color div 256) mod 256)" />
</green>
<blue>
<xsl:value-of select="floor(($color div 65536) mod 256)" />
</blue>
<alpha>
<xsl:value-of select="floor(($color div 16777216) mod 256)" />
</alpha>
The problem with this apprach is that XSLT use floating point arithmentic, so it is possible that rounding errors cause wrong values for the components in some cases.
it's a little bit late to answer the question, but it's for anyone who can have the same problem,
for the indenation issue, I had to use custom xsl templates to make sure the output xml is indentend in the way I want.
the template includes many dirty code that forces xml tags to have special spacing that simulates the indentation
hope this help :)

Categories