XSLT: How to find source and target Xpath for the edge? - xslt

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>

Related

Missing descendant node from XSLT

As per http://xsltfiddle.liberty-development.net/jxNakA5/1, I am missing the following descendant node in the output:
<test-child>123</test-child>
Any idea what can be wrong here?
Source XML
?xml version="1.0" encoding="UTF-8"?>
<library>
<content content-id="a">
<test>
<test-child>123</test-child>
</test>
<data xml:lang="x-default">{"product" : 123 }</data>
<content-links>
<content-link content-id="a1"/>
<content-link content-id="a2"/>
</content-links>
</content>
<content content-id="b">
<data xml:lang="x-default">{"product" : 123 }</data>
<content-links>
<content-link content-id="b1"/>
<content-link content-id="b2"/>
</content-links>
</content>
<content content-id="a1">
<data xml:lang="x-default">{"product" : 123 }</data>
<content-links>
<content-link content-id="a11"/>
<content-link content-id="a12"/>
</content-links>
</content>
<content content-id="a2">
<data xml:lang="x-default">{"product" : 123 }</data>
<content-links>
<content-link content-id="a21"/>
<content-link content-id="a22"/>
</content-links>
</content>
<content content-id="a11">
<data xml:lang="x-default">{"product" : 123 }</data>
</content>
<content content-id="a12"/>
<content content-id="a21">
<data xml:lang="x-default">{"product" : 123 }</data>
</content>
</library>
XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:function name="mf:get-related-elements" as="element()*">
<xsl:param name="element" as="element()"/>
<xsl:sequence
select="$element ! (. | * | key('ref', content-links/content-link/#content-id)/mf:get-related-elements(.))"/>
</xsl:function>
<xsl:key name="ref" match="/*//*" use="#content-id"/>
<xsl:param name="cid" select="'a'" />
<xsl:variable name="start" select="key('ref', $cid)"/>
<xsl:variable name="related-elements" select="mf:get-related-elements($start)"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/*//*[not(. intersect $related-elements)]"/>
</xsl:stylesheet>
See whether using
<xsl:sequence
select="$element ! (. | .//* | key('ref', content-links/content-link/#content-id)/mf:get-related-elements(.))"/>
works for your latest requirement.
But it is hard to tell whether the original approach patched each time you add a requirement is the right way to go unless you start describing with some sentences what the criteria are to copy and not to copy nodes through to the output.
Well, it's pretty clear why it isn't being copied: it's not a "related element".
I don't know what you're trying to achieve (you haven't told us), but your code is treating children as related, but not deeper descendants. The function follows the relationship through #content-id recursively, but it doesn't recurse when selecting the children of the supplied element, so only the first-level children are selected.

regex/XSLT: replace specific string within boundaries [duplicate]

This question already has answers here:
Why it's not possible to use regex to parse HTML/XML: a formal explanation in layman's terms
(10 answers)
Closed 3 years ago.
I am not sure how to use regex to do the following:
within the edge tags (), check if type="highway.secondary", and if yes, replace all values of speed to 40.
<edge id="-100396051#2" type="highway.unclassified">
<lane id="-100396051#2_0" index="0" speed="13.89">
<param key="origId" value="100396051"/>
</lane>
</edge>
<edge id="-101784374#0" type="highway.secondary">
<lane id="-101784374#0_0" index="0" speed="27.78" length="17.22" >
<param key="origId" value="101784374"/>
</lane>
<lane id="-101784374#0_1" index="1" speed="29.98" length="17.22" >
<param key="origId" value="101784374"/>
</lane>
</edge>
So far I got this:
(?<=type="highway\.secondary")(speed)(?=edge), but that doesn't find speed...
Thanks!
INPUT: edges.xml
<edges>
<edge id="-100396051#2" type="highway.unclassified">
<lane id="-100396051#2_0" index="0" speed="13.89">
<param key="origId" value="100396051"/>
</lane>
</edge>
<edge id="-101784374#0" type="highway.secondary">
<lane id="-101784374#0_0" index="0" speed="27.78" length="17.22" >
<param key="origId" value="101784374"/>
</lane>
<lane id="-101784374#0_1" index="1" speed="29.98" length="17.22" >
<param key="origId" value="101784374"/>
</lane>
</edge>
</edges>
STYLESHEET: change_speed.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="#speed[../parent::edge and ../../#type='highway.secondary']">
<xsl:attribute name="speed">
<xsl:value-of select="'40'"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
OUTPUT:
$ xsltproc change_speed.xsl edges.xml
<?xml version="1.0" encoding="utf-8"?>
<edges>
<edge id="-100396051#2" type="highway.unclassified">
<lane id="-100396051#2_0" index="0" speed="13.89">
<param key="origId" value="100396051"/>
</lane>
</edge>
<edge id="-101784374#0" type="highway.secondary">
<lane id="-101784374#0_0" index="0" speed="40" length="17.22">
<param key="origId" value="101784374"/>
</lane>
<lane id="-101784374#0_1" index="1" speed="40" length="17.22">
<param key="origId" value="101784374"/>
</lane>
</edge>
</edges>
Explanations:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
Will copy all the nodes and attributes recursively
<xsl:template match="#speed[../parent::edge and ../../#type='highway.secondary']">
<xsl:attribute name="speed">
<xsl:value-of select="'40'"/>
</xsl:attribute>
</xsl:template>
When you reach an attribute named speed for which the parent node is named edge and has an attribute type whose value is at highway.secondary, change the value of this attribute to 40.

What is the right way to use the proper tag names using XSLT to transform a graphml? [duplicate]

This question already has answers here:
XSLT Transform doesn't work until I remove root node
(2 answers)
Closed 5 years ago.
I'm having a problem to transform my graphml to a HTML using XSLT.
The transformation I'm trying to do is pretty simple but I can't understand what I'm doing wrong at this point.
This is my graphml that I want to transform:
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key for="node" id="d0" yfiles.type="nodegraphics"/>
<key for="edge" id="d1" yfiles.type="edgegraphics"/>
<graph id="dependencies" edgedefault="directed">
<node id="2086673744">
<data key="d0">
<y:ShapeNode>
<y:NodeLabel>com.quadreal.mulesoft.services:qr-identitymgmt-services:mule:3.0.0-SNAPSHOT</y:NodeLabel>
</y:ShapeNode>
</data>
</node>
<node id="1296670053">
<data key="d0">
<y:ShapeNode>
<y:NodeLabel>com.quadreal.mulesoft.context:quadreal-runtime-context:jar:1.0.0-SNAPSHOT:compile</y:NodeLabel>
</y:ShapeNode>
</data>
</node>
<edge source="2086673744" target="1296670053">
<data key="d1">
<y:PolyLineEdge>
<y:EdgeLabel>compile</y:EdgeLabel>
</y:PolyLineEdge>
</data>
</edge>
<node id="826245889">
<data key="d0">
<y:ShapeNode>
<y:NodeLabel>com.quadreal.mulesoft.library:qr-common-error-library:jar:2.0.0-SNAPSHOT:compile</y:NodeLabel>
</y:ShapeNode>
</data>
</node>
<node id="1556730832">
<data key="d0">
<y:ShapeNode>
<y:NodeLabel>com.quadreal.mulesoft.notification:quadreal-utility-common-domains:jar:3.0.0-SNAPSHOT:compile</y:NodeLabel>
</y:ShapeNode>
</data>
</node>
<edge source="826245889" target="1556730832">
<data key="d1">
<y:PolyLineEdge>
<y:EdgeLabel>compile</y:EdgeLabel>
</y:PolyLineEdge>
</data>
</edge>
<edge source="2086673744" target="826245889">
<data key="d1">
<y:PolyLineEdge>
<y:EdgeLabel>compile</y:EdgeLabel>
</y:PolyLineEdge>
</data>
</edge>
</graph>
</graphml>
This is my XSL that will be used to transform the graphml:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<h1>Dependencies:</h1>
<table border="1" width="300">
<tr><th>Package Name</th><th>Dependencies</th></tr>
<xsl:apply-templates select="/graphml/graph/*"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="node">
<tr><td><xsl:value-of select="data/ShapeNode/NodeLabel"/></td><td>TBD</td></tr>
</xsl:template>
</xsl:stylesheet>
And this is the output I'm getting:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><META http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body><div>
<h1>Dependencies:</h1>
<table border="1" width="300"><tr>
<th>Package Name</th>
<th>Dependencies</th>
</tr></table>
</div>
</body></html>
I tried in many different ways to have this one working but they didn't work.
Why it's not matching and applying the node template to add the rows properly?
Thank you!
It doesn't match because node is in namespace xmlns="http://graphml.graphdrawing.org/xmlns"
Declare the same namespace in your stylesheet (with a non-empty prefix) and use it in the xpath expressions
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:g="http://graphml.graphdrawing.org/xmlns"
xmlns:y="http://www.yworks.com/xml/graphml">
...
<xsl:apply-templates select="/g:graphml/g:graph/*"/>
...
<xsl:template match="g:node">
<tr><td><xsl:value-of select="g:data/y:ShapeNode/y:NodeLabel"/></td><td>TBD</td></tr>
</xsl:template>
...

xslt 1.0 - find unique values by several criteria

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

XSLT: Merge a set of tree hierarchies

I have an XML document based what Excel produces when saving as "XML Spreadsheet 2003 (*.xml)".
The spreadsheet itself contains a header section with a hierarchy of labels:
| A B C D E F G H I
-+-----------------------------------------------------
1| a1 a2
2| a11 a12 a13 a21 a22
3| a111 a112 a121 a122 a131 a132 a221 a222
This hierarchy is present on all sheets in the workbook, and looks more or less the same everywhere.
Excel XML works exactly like ordinary HTML tables. (<row>s that contain <cell>s). I have been able to transform everything into such a tree structure:
<node title="a1" col="1">
<node title="a11" col="1">
<node title="a111" col="1"/>
<node title="a112" col="2"/>
</node>
<node title="a12" col="3">
<node title="a121" col="3" />
<node title="a122" col="4" />
</node>
<!-- and so on -->
</node>
But here is the complication:
there is more than one worksheet, so there is a tree for each of them
the hierarchy may be slightly different on each sheet, the trees will not be equal (for example, sheet 2 may have "a113", while the others don't)
tree depth is not explicitly limited
the labels however are meant to be the same across all sheets, which means they can be used for grouping
I'd like to merge these separate trees into one that looks like this:
<node title="a1">
<col on="sheet1">1</col>
<col on="sheet2">1</col>
<node title="a11">
<col on="sheet1">1</col>
<col on="sheet2">1</col>
<node title="a111">
<col on="sheet1">1</col>
<col on="sheet2">1</col>
</node>
<node title="a112">
<col on="sheet1">2</col>
<col on="sheet2">2</col>
</node>
<node title="a113"><!-- different here -->
<col on="sheet2">3</col>
</node>
</node>
<node title="a12">
<col on="sheet1">3</col>
<col on="sheet2">4</col>
<node title="a121">
<col on="sheet1">3</col>
<col on="sheet2">4</col>
</node>
<node title="a122">
<col on="sheet1">4</col>
<col on="sheet2">5</col>
</node>
</node>
<!-- and so on -->
</node>
Ideally I'd like to be able to do the merge before I even build the three structure from the Excel XML (if you get me started on this, it'd be great). But since I have no idea how I would do this, a merge after the trees have been built (i.e.: the situation described above) will be fine.
Thanks for your time. :)
Here is one possible solution in XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<t>
<xsl:apply-templates
select="node[#title='a1'][1]">
<xsl:with-param name="pOther"
select="node[#title='a1'][2]"/>
</xsl:apply-templates>
</t>
</xsl:template>
<xsl:template match="node">
<xsl:param name="pOther"/>
<node title="{#title}">
<col on="sheet1">
<xsl:value-of select="#col"/>
</col>
<xsl:choose>
<xsl:when test="not($pOther)">
<xsl:apply-templates mode="copy">
<xsl:with-param name="pSheet" select="'sheet1'"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<col on="sheet2">
<xsl:value-of select="$pOther/#col"/>
</col>
<xsl:for-each select=
"node[#title = $pOther/node/#title]">
<xsl:apply-templates select=".">
<xsl:with-param name="pOther" select=
"$pOther/node[#title = current()/#title]"/>
</xsl:apply-templates>
</xsl:for-each>
<xsl:apply-templates mode="copy" select=
"node[not(#title = $pOther/node/#title)]">
<xsl:with-param name="pSheet" select="'sheet1'"/>
</xsl:apply-templates>
<xsl:apply-templates mode="copy" select=
"$pOther/node[not(#title = current()/node/#title)]">
<xsl:with-param name="pSheet" select="'sheet2'"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</node>
</xsl:template>
<xsl:template match="node" mode="copy">
<xsl:param name="pSheet"/>
<node title="{#title}">
<col on="{$pSheet}">
<xsl:value-of select="#col"/>
</col>
<xsl:apply-templates select="node" mode="copy">
<xsl:with-param name="pSheet" select="$pSheet"/>
</xsl:apply-templates>
</node>
</xsl:template>
</xsl:stylesheet>
When the above transformation is applied on this XML document (the concatenation of the two XML documents under a common top node -- left as an exercise for the reader :) ):
<t>
<node title="a1" col="1">
<node title="a11" col="1">
<node title="a111" col="1"/>
<node title="a112" col="2"/>
</node>
<node title="a12" col="3">
<node title="a121" col="3" />
<node title="a122" col="4" />
</node>
<!-- and so on -->
</node>
<node title="a1" col="1">
<node title="a11" col="1">
<node title="a111" col="1"/>
<node title="a112" col="2"/>
<node title="a113" col="3"/>
</node>
<node title="a12" col="4">
<node title="a121" col="4" />
<node title="a122" col="5" />
</node>
<!-- and so on -->
</node>
</t>
The wanted result is produced:
<t>
<node title="a1">
<col on="sheet1">1</col>
<col on="sheet2">1</col>
<node title="a11">
<col on="sheet1">1</col>
<col on="sheet2">1</col>
<node title="a111">
<col on="sheet1">1</col>
<col on="sheet2">1</col>
</node>
<node title="a112">
<col on="sheet1">2</col>
<col on="sheet2">2</col>
</node>
<node title="a113">
<col on="sheet2">3</col>
</node>
</node>
<node title="a12">
<col on="sheet1">3</col>
<col on="sheet2">4</col>
<node title="a121">
<col on="sheet1">3</col>
<col on="sheet2">4</col>
</node>
<node title="a122">
<col on="sheet1">4</col>
<col on="sheet2">5</col>
</node>
</node>
</node>
</t>
Do note the following:
We suppose that both top node elements have "a1" as the value of their title attribute. This can easily be generalized.
The template matching node has a parameter named pOther, which is the corresponding element named node from the other document. This template is applied - to only if $pOther exists.
When no corresponding element named node exists, another template, also matching node, but in mode copy is applied. This template has a parameter named pSheet, the value of which is the sheet name (string) this element belongs to.
How about a callable template taking the sheet number as a parameter, which examines the input and returns the correct "col" node if it appears in that sheet's XML, and nothing if it doesn't. At each node, call it once for each sheet.
To merge the trees, maybe a template that looks for all children of the current node in any sheet, and recurses on itself for each of them.
Sorry no sample code, I find writing XSLT to be pretty slow, probably because I don't do it often. So I may well have missed something crucial. But putting it all together would give something like:
get the title of "/node". With that title:
search all sheets for this title, emitting the "col" node for each
search all sheets for children of nodes with this title (discarding duplicates)
recurse on each of those titles.
Here are some snippets for removing duplicates in various ways:
http://www.dpawson.co.uk/xsl/sect2/N2696.html
Reading multiple documents is processor-dependent, but if all else fails a bit of cut-and-pastery with any old scripting language would probably do, provided that you know they'll all have the same encoding, don't use conflicting ids, and so on.