I have following XML:
<library>
<elements>
<element name="books">
<property name="author">A</property>
<property name="select">true</property>
</element>
<element name="books">
<property name="author">B</property>
<property name="select">false</property>
</element>
<element name="books">
<property name="author">C</property>
<property name="select">true</property>
</element>
<element name="books">
<property name="author">A</property>
<property name="select">true</property>
</element>
</elements>
</library>
I need to get output of all elements with name="books", which are selected (selected = true) and unique by author name.
Must use xslt 1.0.
Expected result:
author: A
author: C
Must output data only for authors A and C.
Thanks in advance!
not(.=preceding::*)
to retrieve the unique values of the given xpath in for loop
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<ul>
<xsl:for-each select="//elements/element[#name = 'books' and property[#name = 'select' and .='true'] ]/property[#name = 'author' and not(.=preceding::*)]">
<li>
<xsl:value-of select="concat('author :',.)"/>
</li>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>
OUTPUT XML :
author :A author :C
Related
I want to write an xslt file to transfer an xmi file in a graphical file. But I meet the problem that the edge can not connect the right source node and target node. I have tried already two weeks. But I am still confused. Please help me. Thanks a million.
The original code is:
<?xml version="1.0" encoding="UTF-8"?>
<xml xmlns:xmi="#">
<element xmi:id="BasicElement-Line1" name="Line1" xmi:type="association"/>
<element xmi:id="BasicElement-Line2" name="Line2" xmi:type="association"/>
<element xmi:id="BasicElement-Object1" name="Object1" xmi:type="class">
<ownedAttribute xmi:type="Property" name="input" type="BasicElement-Object2" association="BasicElement-Line1"/>
<ownedAttribute xmi:type="Property" name="output" type="BasicElement-Object3" association="BasicElement-Line2"/>
</element>
<element xmi:id="BasicElement-Object2" name="Object2" xmi:type="class">
</element>
<element xmi:id="BasicElement-Object3" name="Object3" xmi:type="class">
</element>
</xml>
and my aim code is:
<?xml version="1.0" encoding="UTF-8"?>
<xmi xmlns:y="##">
<edge target="N1002D" source="N1001B" id="N10005">
<y:PolyLineEdge>
<y:Arrows target="none" source="none" />
</y:PolyLineEdge>
</edge>
<edge target="N1002D" source="N1001B" id="N10010">
<y:PolyLineEdge>
<y:Arrows target="none" source="none" />
</y:PolyLineEdge>
</edge>
<node id="N1001B">
<y:NodeLabel>BasicElement-Object1</y:NodeLabel>
</node>
<node id="N1002D">
<y:NodeLabel>BasicElement-Object2</y:NodeLabel>
</node>
<node id="N10033">
<y:NodeLabel>BasicElement-Object3</y:NodeLabel>
</node>
</xmi>
Because there will be more "class" element in the future. So I used "{generate-id()}" to define the node IDs. But when I do that, I found the edge can not find the way of source node and target node. So I have already worked on it two weeks and have no idea on it. Please help me, I really appreciate.
I'm not really familiar with XMI and the target format, but here's something that should fit your description.
Source:
<?xml version="1.0" encoding="UTF-8"?>
<xml xmlns:xmi="#">
<element xmi:id="BasicElement-Line1" name="Line1" xmi:type="association">
<ownedEnd xmi:type="Property" type="BasicElement-Object1" association="BasicElement-Line1"/>
</element>
<element xmi:id="BasicElement-Line2" name="Line2" xmi:type="association">
<ownedEnd xmi:type="Property" type="BasicElement-Object1" association="BasicElement-Line2"/>
</element>
<element xmi:id="BasicElement-Object1" name="Object1" xmi:type="class">
<ownedAttribute xmi:type="Property" name="input" type="BasicElement-Object2" association="BasicElement-Line1"/>
<ownedAttribute xmi:type="Property" name="output" type="BasicElement-Object3" association="BasicElement-Line2"/>
</element>
<element xmi:id="BasicElement-Object2" name="Object2" xmi:type="class">
</element>
<element xmi:id="BasicElement-Object3" name="Object3" xmi:type="class">
</element>
</xml>
Transformed with (adjust the namespaces to the correct uris):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xmi="#" xmlns:y="##"
exclude-result-prefixes="xmi" version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="xml">
<xmi>
<xsl:apply-templates select="element"/>
</xmi>
</xsl:template>
<xsl:template match="element[#xmi:type='class']">
<node id="{generate-id()}">
<y:NodeLabel>
<xsl:value-of select="#xmi:id"/>
</y:NodeLabel>
<y:UMLClassNode/>
</node>
</xsl:template>
<xsl:template match="element[#xmi:type='association']">
<!-- association name -->
<xsl:variable name="association" select="ownedEnd/#association"/>
<!-- id of source -->
<xsl:variable name="ownedEnd-type" select="ownedEnd/#type"/>
<!-- using association variable to select the correct id of target -->
<xsl:variable name="ownedAttribute-type"
select="//element[#xmi:id = $ownedEnd-type]/ownedAttribute[#association = $association]/#type"/>
<edge id="{ generate-id() }"
source="{ generate-id( /xml/element[#xmi:id = $ownedEnd-type] ) }"
target="{ generate-id( /xml/element[#xmi:id = $ownedAttribute-type] ) }">
<y:PolyLineEdge>
<y:Arrows source="none" target="none"/>
</y:PolyLineEdge>
</edge>
</xsl:template>
</xsl:stylesheet>
gives you:
<xmi xmlns:y="##">
<edge id="d0e3" source="d0e13" target="d0e20">
<y:PolyLineEdge>
<y:Arrows source="none" target="none"/>
</y:PolyLineEdge>
</edge>
<edge id="d0e8" source="d0e13" target="d0e23">
<y:PolyLineEdge>
<y:Arrows source="none" target="none"/>
</y:PolyLineEdge>
</edge>
<node id="d0e13">
<y:NodeLabel>BasicElement-Object1</y:NodeLabel>
<y:UMLClassNode/>
</node>
<node id="d0e20">
<y:NodeLabel>BasicElement-Object2</y:NodeLabel>
<y:UMLClassNode/>
</node>
<node id="d0e23">
<y:NodeLabel>BasicElement-Object3</y:NodeLabel>
<y:UMLClassNode/>
</node>
</xmi>
I have an input which can sometimes have value and sometimes not. Like value1=ABC or value1=""
During my xsl transformation I have my code with the following line
<element name="test"><xsl:value-of select="$value1"/><element>
The output of the above code when value is present is
<element name="test">ABC</element>
When the value is not present, the output is
<element name="test"/>
Now, I want it to look like
<element name="test"></element>
instead of
<element name="test"/>
Is it possible to get the required output?
If yes, then how to do it?
Try adding <xsl:output method="html"/>.
Example...
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:variable name="test1"/>
<xsl:variable name="test2" select="'value'"/>
<element name="test1"><xsl:value-of select="$test1"/></element>
<element name="test2"><xsl:value-of select="$test2"/></element>
</xsl:template>
</xsl:stylesheet>
produces (when applied to any XML instance):
<element name="test1"></element>
<element name="test2">value</element>
I have no formal training on XSL and am totally new to it. Basically I have a XML file as follows:
<document document="wpc_article_video_qp">
<properties>
<property type="name" prop_ns="http://sapportals.com/xmlns/cm" prop_name="displayname"/>
<property type="createdBy">USER.PRIVATE_DATASOURCE.un:LU23921</property>
<property type="includeInRSS" prop_ns="wpc_wcm" prop_name="wpc_wcm_rss"/>
<property type="displayNewIcon" prop_ns="wpc_wcm" prop_name="wpc_wcm_new"/>
</properties>
<elements>
<element type="videotitle">TestTitle</element>
<element type="videopath">ICT/LB_1152kbps.mp4</element>
<element type="videowidth">500</element>
<element type="videoheight">250</element>
</elements>
<relatedlinks/>
<relatedfiles/>
</document>
I have no control on the XML. What I mean is that the XML is generated by a tool and I cannot change it. What I am trying to do now is to write XSL which should generate a anchor tag as follows:
<a style="display:block;width:500px;height:250px" id="player" href="ICT/LB_1152kbps.mp4"></a>
where the href, width and height are picked up from "videopath", "videowidth" and "videoheight" XML elements respectively.
I tried to search on this website and some of the other websites but as I said since I am totally new to XSL, I don't really kwow where to begin. Any help would be highly appreciated.
If you're looking for a good place to begin, I'd recommend picking up an XSLT book in a library or so, or have a look at some online tutorial like the one by Zvon. When writing XSLT, I highly recommend a spezicalized editor lixe oXygen. It not only saves you from typing a lot with its autocempletion features and by closing tags automatically, it also checks whether all the program code and XPath syntax are valid.
The question here is is: You probably generate a whole HTML document, not only a single anchor tag. So, here is a template that generates the anchor tag by matching the <element> element, but has to be integrated into a whole stylesheet:
<xsl:template match="element">
<a style="display:block;
width:{element/#videowidth}px;
height:{element/#videoheight}px"
id="player" href="{element/#videopath}"></a>
</xsl:template>
Edit: If you really want an output document that just contains the anchor tag: The XSLT processor starts processing the input document at the root node and then steps through the elements as you tell it using <xsl:apply-templates> (or <xsl:for-each>). If you want that the template matching <element> to actually kick in, you have to "move" to that context from the document element context:
<xsl:template match="/">
<xsl:apply-templates select="document/elements/element"/>
</xsl:template>
Here is how to do this, using AVT (Attribute Value Templates):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="elements">
<xsl:copy>
<a style="display:block;
width:{*[#type='videowidth']}px;
height:{*[#type='videoheight']}px"
id="player" href="{*[#type='videopath']}"></a>
</xsl:copy>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Do note:
The CSS properties above are on a new line each -- this is done for readability. In a real transformation one may want to keep them without intervening space -- to produce the wanted result without spaces.
When this transformation is applied on the provided XML document:
<document document="wpc_article_video_qp">
<properties>
<property type="name" prop_ns="http://sapportals.com/xmlns/cm" prop_name="displayname"/>
<property type="createdBy">USER.PRIVATE_DATASOURCE.un:LU23921</property>
<property type="includeInRSS" prop_ns="wpc_wcm" prop_name="wpc_wcm_rss"/>
<property type="displayNewIcon" prop_ns="wpc_wcm" prop_name="wpc_wcm_new"/>
</properties>
<elements>
<element type="videotitle">TestTitle</element>
<element type="videopath">ICT/LB_1152kbps.mp4</element>
<element type="videowidth">500</element>
<element type="videoheight">250</element>
</elements>
<relatedlinks/>
<relatedfiles/>
</document>
the wanted, correct result is produced:
<elements>
<a style="display:block;width:500px;height:250px" id="player" href="ICT/LB_1152kbps.mp4"/>
</elements>
I have XML data
<logData>
<log>
<id>1</id>
</log>
<log>
<id>2</id>
</log>
<log>
<id>3</id>
</log>
<log>
<id>4</id>
</log>
</logData>
I want get only part of logs using xslt transformation using fn:subsequence function
here is my xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:fn="http://www.w3.org/2006/xpath-functions" version="1.0" >
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/logData" >
<xsl:element name="log">
<xsl:copy-of select="fn:subsequence(./log, 2, 3)"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
and I get
ERROR: 'The first argument to the non-static Java function 'subsequence' is not a valid object reference.'
I am using Java transformation API, part of Java SE 1.6.
Can you help me?
<xsl:copy-of select="fn:subsequence(./log, 2, 3)"/>
The function subsequence() is defined in XPath 2.0 and is available only in an XSLT 2.0 processor.
In XSLT 1.0 use:
<xsl:copy-of select="log[position() > 1 and not(position() > 4)]"/>
Here is a complete transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy-of select="log[position() > 1 and not(position() > 4)]"/>
</xsl:template>
</xsl:stylesheet>
When this is applied on the provided XML document:
<logData>
<log>
<id>1</id>
</log>
<log>
<id>2</id>
</log>
<log>
<id>3</id>
</log>
<log>
<id>4</id>
</log>
</logData>
the wanted, correct result is produced:
<log>
<id>2</id>
</log>
<log>
<id>3</id>
</log>
<log>
<id>4</id>
</log>
Since you are using Java, all you need to do is to ensure that your code loads an XSLT 2.0 processor instead of XSLT 1.0. The default XSLT processor in the JDK only supports XSLT 1.0.
Download Saxon-HE, and set the system property
-Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl
and your code should work.
(Of course, as Dimitre shows, this transformation can be done easily enough in XSLT 1.0. But by sticking to XSLT 1.0 you are are trying to move with your feet tied together at the ankles. XSLT 2.0 is vastly more powerful and easier to use, and it's available in your environment, so use it.)
I am trying to find the last element in my xml, which looks like:
<list>
<element name="A" />
<element name="B" >
<element name="C" />
<element name="D" >
<element name="D" />
<element name="E" />
<element name="F" />
<element name="G" />
</element>
<element name="H" />
<element name="I" />
</list>
I need to get some kind of reverse menu, where current element and parents are highlighted as "active" and sibling as "inactive". Instead in result I have a messy tree only when I suppose "D" element clicked.
Double D elements are my problem. When I use
select="//element[#name='D'][last()]" or select="//element[#name='D' and last()]" (btw which one is correct?) first time first occurrence of D element is selected (debugger shows that). Here is xsl
<xsl:template match="list">
<xsl:apply-templates select="//navelement[#name = 'D'][last()]" mode="active"/>
</xsl:template>
<xsl:template match="element">
<ul class="menu">
<xsl:apply-templates select="preceding-sibling::node()" mode="inactive"/>
<li><a>....</a></li>
<xsl:apply-templates select="following-sibling::node()" mode="inactive"/>
</ul>
<xsl:apply-templates select="parent::element" mode="active"/>
</xsl:template>
<xsl:template match="element" mode="inactive">
<li><a>....</a></li>
</xsl:template>
You need to put the last() indexing on the nodelist result, rather than as part of the selection criteria. Try:
(//element[#name='D'])[last()]