Reputation: 3
I have two nodes that incidentally share a subset of their data. I need to sort the first set based on the original order of the second.
To give an example, consider the following sets of points and lines. Every "start" and "end" point for each line will in every case also be listed in the Points list.
<Points>
<Point name="Point1">0 0</Point>
<Point name="Point2">0 1</Point>
<Point name="Point3">1 1</Point>
<Point name="Point4">1 0</Point>
</Points>
<Shape>
<Line name="Line1">
<Start>0 0</Start>
<End>0 1</End>
</Line>
<Line name="Line2">
<Start>0 1</Start>
<End>1 1</End>
</Line>
<Line name="Line3">
<Start>1 1</Start>
<End>1 0</End>
</Line>
<Line name="Line4">
<Start>1 0</Start>
<End>0 0</End>
</Line>
</Shape>
I need to sort Points so that the values match the same order as the End points listed for the Shape.
For the above example, the expected XSLT output would be as follows...
Point2 0 1
Point3 1 1
Point4 1 0
Point1 0 0
To be specific, I'm working with COGO Points and Alignment objects from AutoCAD, trying to customize reports based on LandXML outputs from Civil3D. The sample above is just to give me some direction. Any help would be greatly appreciated.
Upvotes: 0
Views: 118
Reputation: 116992
there will always be the same number of points as ends.
In such case, the problem could be restated as:
List all the end points in document order, along with the corresponding point name.
And the best technique to achieve this would be to use a key - for example (simplified to illustrate the principle):
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="pt" match="Point" use="." />
<xsl:template match="/root">
<xsl:copy>
<xsl:for-each select="Shape/Line">
<xsl:copy-of select="key('pt', End)"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Point name="Point2">0 1</Point>
<Point name="Point3">1 1</Point>
<Point name="Point4">1 0</Point>
<Point name="Point1">0 0</Point>
</root>
Upvotes: 1
Reputation: 21641
I think this should work: a template that matches End
nodes and grabs the Point
node with the same text value (there's a second template matching all text()
to make sure it doesn't get outputted unintentionally):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/root/Shape/Line/End">
<xsl:variable name="endPtText" select="./text()" />
<xsl:value-of select="/root/Points/Point[text() = $endPtText]/@name" /><xsl:text> </xsl:text><xsl:value-of select="$endPtText"/> <xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="text()" />
</xsl:transform>
Produces:
Point2 0 1
Point3 1 1
Point4 1 0
Point1 0 0
from:
<root>
<Points>
<Point name="Point1">0 0</Point>
<Point name="Point2">0 1</Point>
<Point name="Point3">1 1</Point>
<Point name="Point4">1 0</Point>
</Points>
<Shape>
<Line name="Line1">
<Start>0 0</Start>
<End>0 1</End>
</Line>
<Line name="Line2">
<Start>0 1</Start>
<End>1 1</End>
</Line>
<Line name="Line3">
<Start>1 1</Start>
<End>1 0</End>
</Line>
<Line name="Line4">
<Start>1 0</Start>
<End>0 0</End>
</Line>
</Shape>
</root>
Just note that if you have two identical Point
text nodes this won't work, and if you don't have a Point
node corresponding to an End
node this won't work.
Upvotes: 0