keeping characters un-escaped in attributes when processing with XSLT - xslt

I'm transforming an ivy.xml file with XSLT, I basically want to update the rev attribute of a specific dependency tag. My problem is with the conf attribute, I want it to stay exactly the same, unfortunately > gets converted to >. My ivy file looks like this:
<ivy-module version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="foo" module="libfoo" revision="1.0.1"/>
<configurations>
</configurations>
<publications>
<artifact name="libfoo" type="jar" conf="default" />
</publications>
<dependencies>
<dependency org="easymock" name="easymock" rev="2.5.2" conf="test->default,class-extension"/>
</dependencies>
</ivy-module>
my XSL looks like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output />
<xsl:param name="dependency.rev" />
<xsl:param name="dependency.org" />
<xsl:param name="dependency.name" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="dependency">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
<xsl:choose>
<xsl:when test="#name=$dependency.name">
<xsl:attribute name="rev">
<xsl:value-of select="$dependency.rev" />
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
and my output looks like this:
<dependency org="easymock" name="easymock" rev="2.5.2" conf="test->default,class-extension"/>
I've tried setting disable-output-escaping="no" on an explicit set of the conf attribute, but that didn't work
<xsl:attribute name="conf">
<xsl:value-of select="#conf" disable-output-escaping="yes" />
</xsl:attribute>

My problem is with the conf attribute,
I want it to stay exactly the same,
unfortunately > gets converted to
>.
You can't. As per the W3 XSLT Spec:
"It is an error for output escaping to be disabled for a text node that is used for something other than a text node in the result tree. Thus, it is an error to disable output escaping for an xsl:value-of or xsl:text element that is used to generate the string-value of a comment, processing instruction or attribute node"
There is nothing problematic in the conf attribute containing test->.
In fact, both
conf="test->default,class-extension"
and
conf="test->default,class-extension"
have exactly the same string value.
To verify this, just apply the following simple transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*">
"<xsl:value-of select="#conf"/>"
</xsl:template>
</xsl:stylesheet>
to the "bad" result:
<dependency org="easymock"
name="easymock"
rev="2.5.2"
conf="test->default,class-extension"
/>
and what you get is:
"test->default,class-extension"
This is exactly the same as coding   instead of -- these are exactly the same characters, but specified in different ways.

Related

Store condition test in variable

I am quite new to XSLT and need to transform an address xml file.
I need to store the result of an condition test in a variable, I want to use it later in the same template.
Here my source xml:
<?xml version="1.0" encoding="UTF-8"?>
<message>
<partner>
<addressInfo>
<addressUsage>
<Code>Default</Code>
<Main>false</Main>
</addressUsage>
<addressUsage>
<Code>BILL_TO</Code>
<Main>false</Main>
</addressUsage>
<Address>...</Address>
</addressInfo>
<addressInfo>
<addressUsage>
<Code>SHIP_TO</Code>
<Main>false</Main>
</addressUsage>
<addressUsage>
<Code>BILL_TO</Code>
<Main>true</Main>
</addressUsage>
<Address>...</Address>
</addressInfo>
</partner>
</message>
Now, I need to know if there is an address with code "BILL_TO" and Main = "true". I need to store this in a variable. But in my test I always fail.
Here my 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" omit-xml-declaration="yes"/>
<!-- copy all nodes and attributes -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="partner">
<partner>
<xsl:variable name="main_bill_to">
<xsl:choose>
<xsl:when test="(./addressInfo/addressUsage/Code = 'BILL_TO' and Main = 'true')">
<xsl:value-of select="'Y'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'N'" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:if test="($main_bill_to = 'Y')">
<result>yes</result>
</xsl:if>
<xsl:if test="($main_bill_to = 'N')">
<result>no</result>
</xsl:if>
</partner>
</xsl:template>
</xsl:stylesheet>
I always get the output "no".
<message>
<partner>
<result>no</result>
</partner>
</message>
Any idea? Many thanks!
This expression is not quite right...
<xsl:when test="(./addressInfo/addressUsage/Code = 'BILL_TO' and Main = 'true')">
You are actually asking "Is there an element addressInfo/addressUsage/Code (under partner) equal to 'BILL_TO', and is there also an element Main (under partner) equal to 'true'".
In other words, it is looking for an element named Main under partner, not under the same addressUsage as the Code element you are checking.
You need to do this...
<xsl:when test="addressInfo/addressUsage[Code = 'BILL_TO' and Main = 'true']">
So, this is now asking "Is there an addressInfo/addressUsage element which has both Code set to 'BILL_TO' and Main set to 'true'"?
Note the ./ is not really necessary, which is why I removed it.
Also note, you don't really need to go to all the trouble of using an xsl:choose to set the variable to "Y" or "N". You can, in this case, simplify the code block to this....
<xsl:template match="partner">
<partner>
<xsl:variable name="main_bill_to" select="addressInfo/addressUsage[Code = 'BILL_TO' and Main = 'true']" />
<xsl:if test="$main_bill_to">
<result>yes</result>
</xsl:if>
<xsl:if test="not($main_bill_to)">
<result>no</result>
</xsl:if>
</partner>
</xsl:template>
So now, you are checking if an element exists to determine what to display.

XSLT copy-of - attribute left blank

I am trying to modify an existing XSLT to include the originalfilename attribute of the file node in the input XML as the file attribute of feedback in the output xml. I think I'm misunderstanding the copy-of statement and I would be very extremely grateful for any help. At the moment my desired output is displaying an empty file attribute on the feedback node.
XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<document>
<xsl:for-each select="files/file/segmentpair[Comments/Comment]">
<xsl:apply-templates select="Comments/Comment" />
<xsl:copy-of select="files|file|source|target" />
</xsl:for-each>
</document>
</xsl:template>
<xsl:template match="Comment">
<feedback>
<xsl:attribute name="id">
<xsl:value-of select="count(preceding::Comments)+1" />
</xsl:attribute>
<xsl:attribute name="file">
<xsl:value-of select="files/file/#originalfilename" />
</xsl:attribute>
<xsl:value-of select="." />
</feedback>
</xsl:template>
</xsl:stylesheet>
Input
<files>
<file originalfilename="C:\Users\A\Documents\Studio 2014\Projects\15_002_\de-DE\master\advanced-materials-and-processes-msc-hons.xml">
<segmentpair id="1" locked="False" color="245,222,179" match-value="86">
<source>Advanced Materials and Processes (M.Sc.hons.)</source>
<target>Advanced Materials and Processes (MSc)</target>
<Comments>
<Comment>[ic14epub 20.01.2015 09:28:43] 'hons' taken out (discussion of this still ongoing as far as I'm aware)</Comment>
</Comments>
</segmentpair>
</file>
</files>
Desired Output
<feedback id="1" file="C:\Users\A\Documents\Studio 2014\Projects\15_002_\de-DE\master\advanced-materials-and-processes-msc-hons.xml">[ic14epub 20.01.2015 09:28:43] 'hons' taken out (discussion of this still ongoing as far as I'm aware)</feedback>
<source>Advanced Materials and Processes (M.Sc.hons.)</source>
<target>Advanced Materials and Processes (MSc)</target>
You want the #originalfilename of the first <file> ancestor for that <Comment>.
<xsl:template match="Comment">
<feedback
id="{count(preceding::Comments)+1}"
file="{ancestor::file[1]/#originalfilename}"
>
<xsl:value-of select="." />
</feedback>
</xsl:template>
Note the attribute value templates (curly braces). They can save quite a lot of typing.
You can get your desired output simply by adding a / before files in your <xsl:value-of>
<xsl:value-of select="/files/file/#originalfilename"/>
However, I would recommend to use
<xsl:value-of select="../../../#originalfilename"/>
instead of the absolute path, so it will still works if you have more files.

XSLT: Replace string with Abbreviations

I would like to know how to replace the string with the abbreviations.
My XML looks like below
<concept reltype="CONTAINS" name="Left Ventricular Major Axis Diastolic Dimension, 4-chamber view" type="NUM">
<code meaning="Left Ventricular Major Axis Diastolic Dimension, 4-chamber view" value="18074-5" schema="LN" />
<measurement value="5.7585187646">
<code value="cm" schema="UCUM" />
</measurement>
<content>
<concept reltype="HAS ACQ CONTEXT" name="Image Mode" type="CODE">
<code meaning="Image Mode" value="G-0373" schema="SRT" />
<code meaning="2D mode" value="G-03A2" schema="SRT" />
</concept>
</content>
</concept>
and I am selecting some value from the xml like,
<xsl:value-of select="concept/measurement/code/#value"/>
Now what I want is, I have to replace cm with centimeter. I have so many words like this. I would like to have a xml for abbreviations and replace from them.
I saw one similar example here.
Using a Map in XSL for expanding abbreviations
But it replaces node text, but I have text as attribute. Also, it would be better for me If I can find and replace when I select text using xsl:valueof select instead of having a separate xsl:template. Please help. I am new to xslt.
I have created XSLT v "1.1". For abbreviations I have created XML file as you have mentioned:
Abbreviation.xml:
<Abbreviations>
<Abbreviation>
<Short>cm</Short>
<Full>centimeter</Full>
</Abbreviation>
<Abbreviation>
<Short>m</Short>
<Full>meter</Full>
</Abbreviation>
</Abbreviations>
XSLT:
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" />
<xsl:param name="AbbreviationDoc" select="document('Abbreviation.xml')"/>
<xsl:template match="/">
<xsl:call-template name="Convert">
<xsl:with-param name="present" select="concept/measurement/code/#value"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="Convert">
<xsl:param name="present"/>
<xsl:choose>
<xsl:when test="$AbbreviationDoc/Abbreviations/Abbreviation[Short = $present]">
<xsl:value-of select="$AbbreviationDoc/Abbreviations/Abbreviation[Short = $present]/Full"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$present"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
INPUT:
as you have given <xsl:value-of select="concept/measurement/code/#value"/>
OUTPUT:
centimeter
You just need to enhance this Abbreviation.xml to keep short and full value of abbreviation and call 'Convert' template with passing current value to get desired output.
Here a little shorter version:
- with abbreviations in xslt file
- make use of apply-templates with mode to make usage shorter.
But with xslt 1.0 node-set extension is required.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="abbreviations_txt">
<abbreviation abbrev="cm" >centimeter</abbreviation>
<abbreviation abbrev="m" >meter</abbreviation>
</xsl:variable>
<xsl:variable name="abbreviations" select="exsl:node-set($abbreviations_txt)" />
<xsl:template match="/">
<xsl:apply-templates select="concept/measurement/code/#value" mode="abbrev_to_text"/>
</xsl:template>
<xsl:template match="* | #*" mode="abbrev_to_text">
<xsl:variable name="abbrev" select="." />
<xsl:variable name="long_text" select="$abbreviations//abbreviation[#abbrev = $abbrev]/text()" />
<xsl:value-of select="$long_text"/>
<xsl:if test="not ($long_text)">
<xsl:value-of select="$abbrev"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Setting disable-output-escaping="yes" for every xsl:text tag in the xml

say I have the following xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<display>
<xsl:for-each select="logline_t">
<xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="./line_1" <xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="./line_2" <xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="./line_3" <xsl:text disable-output-escaping="yes">></xsl:text>
</xsl:for-each>
</display>
</xsl:template>
</xsl:stylesheet>
Is there a way to set disable-output-escaping="yes" to all of the xsl:text that appear in the document?
I know there is an option to put
< xsl:output method="text"/ >
and every time something like
& lt;
appears, a < will appear, but the thing is that sometimes in the values of line_1, line_2 or line_3, there is a "$lt;" that I don't want changed (this is, I only need whatever is between to be changed)
This is what I'm trying to accomplish. I have this xml:
<readlog_l>
<logline_t>
<hora>16:01:09</hora>
<texto>Call-ID: 663903<hola>396#127.0.0.1</texto>
</logline_t>
</readlog_l>
And this translation:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<display>
<screen name="<xsl:value-of select="name(.)"/>">
<xsl:for-each select="logline_t">
< field name="<xsl:for-each select="*"><xsl:value-of select="."/></xsl:for-each>" value="" type="label"/>
</xsl:for-each>
</screen>
</display>
</xsl:template>
</xsl:stylesheet>
I want this to be the output:
<?xml version="1.0"?>
<display>
<screen name="readlog_l">
<field name="16:01:09 Call-ID: 663903<hola>396#127.0.0.1 " value="" type="label">
</screen>
</display>
Note that I need the "<" inside the field name not to be escaped, this is why I can't use output method text.
Also, note that this is an example and the translations are much bigger, so this is why I'm trying to find out how not to write disable-output-escaping for every '<' or '>' I need.
Thanks!
Thanks for clarifying the question. In this case, I'm fairly sure there's no need to disable output escaping. XSLT was designed to accomplish what you're doing:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<display>
<screen name="{name(.)}">
<xsl:for-each select="logline_t">
<xsl:variable name="nameContent">
<xsl:for-each select="*">
<xsl:if test="position() > 1"><xsl:text> </xsl:text></xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:variable>
<field name="{$nameContent}" value="" type="label" />
</xsl:for-each>
</screen>
</display>
</xsl:template>
</xsl:stylesheet>
I'm a bit unclear on this point:
Note that I need the "<" inside the field name not to be escaped, this is why I can't use output method text.
Which < are you referring to? Is it the < and > around "hola"? If you left those unescaped you would wind up with invalid XML. It also looks like the name attribute in your sample output have a lot of values that aren't in the input XML. Where did those come from?
Given your expected output you don't need d-o-e at all for this. Here is a possible solution that doesn't use d-o-e, and is based on templates rather than for-each:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/*">
<display>
<screen name="{name(.)}">
<xsl:apply-templates select="logline_t"/>
</screen>
</display>
</xsl:template>
<xsl:template match="logline_t">
<field value="" type="label">
<xsl:attribute name="name">
<xsl:apply-templates select="*" mode="fieldvalue"/>
</xsl:attribute>
</field>
</xsl:template>
<xsl:template match="*[last()]" mode="fieldvalue">
<xsl:value-of select="." />
</xsl:template>
<xsl:template match="*" mode="fieldvalue">
<xsl:value-of select="." />
<xsl:text> </xsl:text>
</xsl:template>
</xsl:stylesheet>
If you want to set d-o-e on everything, that suggests you are trying to generate markup "by hand". I don't think that's a particularly good idea (in fact, I think it's a lousy idea), but if it's what you want to do, I would suggest using the text output method instead of the xml output method. That way, no escaping of special characters takes place, and therefore it doesn't need to be disabled.

XSLT - how to apply a template to every node of the type?

I am quite new to xsl and functional programming, so I'll be grateful for help on this one:
I have a template that transforms some xml and provides an output. The problem is that there are many elements of type xs:date, all in different contexts, that must be localized. I use a concatenation of substrings of these xs:dates to produce a localized date pattern strings.
As you can guess this causes a lot of copy-paste "substring-this and substring-that". How can I write a template that will automatically transform all the elements of type xs:date to localized strings preserving all the context-aware transformations?
My xsl is something like this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" encoding="utf-8"/>
<xsl:template match="/">
...
<input value="{substring(/select/a/date 9,2)}.{substring(/select/a/date, 6,2)}.{substring(/select/a/date 1,4)}">
...
<!-- assume that following examples are also with substrings -->
<div><xsl:value-of select="different-path/to_date"/></div>
...
<table>
<tr><td><xsl:value-of select="path/to/another/date"/></td></tr>
</table>
<apply-templates/>
</xsl:template>
<xsl:template match="something else">
<!-- more dates here -->
</xsl:template>
</xsl:stylesheet>
I hope I managed to make my question clear =)
UPD: Here is an example of xml:
<REQUEST>
<header>
<... />
<ref>
<ref_date type="xs:date">1970-01-01</ref_date>
</ref>
</header>
<general>
<.../>
<info>
<.../>
<date type="xs:date">1970-01-01</date>
<ExpireDate type="xs:date">1970-01-01</ExpireDate>
<RealDate type="xs:date">1970-01-01</RealDate>
<templateDetails>template details</templateDetails>
<effectiveDate type="xs:date">1970-01-01</effectiveDate>
</info>
<party>
<.../>
<date type="xs:date">1970-01-01</date>
</party>
<!-- many other parts of such kind -->
</general>
</REQUEST>
As for the output, there are many different options. The main thing is that these values must be set as a value of different html objects, such as tables, input fields and so on. You can see an example in the xsl listing.
P.S. I'm using xsl 1.0.
If you did a schema-aware XSLT 2.0 transformation, you wouldn't need all those type='xs:date' attributes: defining it in the schema as a date would be enough. You could then match the attributes with
<xsl:template match="attribute(*, xs:date)">
What you could do is add a template to match any element which has an #type attribute of 'xs:date', and do you substring manipulation in there
<xsl:template match="*[#type='xs:date']">
<xsl:value-of select="translate(., '-', '/')" />
</xsl:template>
In this case I am just replacing the hyphens by slashes as an example.
Then, instead of using xsl:value-of....
<div><xsl:value-of select="different-path/to_date"/></div>
You could use xsl:apply-templates
<div><xsl:apply-templates select="different-path/to_date"/></div>
Consider this XSLT as an example
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*[#type='xs:date']">
<xsl:copy>
<xsl:value-of select="translate(., '-', '/')" />
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In this case, all this XSLT is doing is copying the XML document as-is, but changing the date elements.
If you wanted to use the date template for other elements, or values, you could also make it a named-template, like so
<xsl:template match="*[#type='xs:date']" name="date">
<xsl:param name="date" select="." />
<xsl:value-of select="translate($date, '-', '/')" />
</xsl:template>
This would allow you to also call it much like a function. For example, to format a data and add as an attribute you could do the following:
<input>
<xsl:attribute name="value">
<xsl:call-template name="date">
<xsl:with-param name="date" select="/select/a/date" />
</xsl:call-template>
</xsl:attribute>
</input>