Reputation: 591
I have an XML file. I should find for same field2 start(min) date and End(max) date from field3. Maybe xsl have some function to find it.Because I try do it trying to find min month and min day and max month, max day. XML:
<document>
<line id="0">
<field id="2">X111</field>
<field id="3">2011-03-31</field>
</line>
<line id="1">
<field id="2">X111</field>
<field id="3">2011-04-04</field>
</line>
<line id="2">
<field id="2">X111</field>
<field id="3">2011-04-02</field>
</line>
<line id="3">
<field id="2">X222</field>
<field id="3">2011-04-04</field>
</line>
<line id="4">
<field id="2">X222</field>
<field id="3">2011-04-01</field>
</line>
<line id="4">
<field id="2">X333</field>
<field id="3">2011-04-01</field>
</line>
</document>
Output:
<document>
<Message>
<ID>X111</ID>
<dateStart>2011-03-31</dateStart>
<dateEnd>2011-04-04</dateEnd>
</Message>
<Message>
<ID>X222</ID>
<dateStart>2011-04-01</dateStart>
<dateEnd>2011-04-04</dateEnd>
</Message>
<Message>
<ID>X333</ID>
<dateStart>2011-04-01</dateStart>
<dateEnd>2011-04-01</dateEnd>
</Message>
</document>
Please help to solve it. I'm working with stylesheet version="1.0".
Upvotes: 2
Views: 3779
Reputation: 60424
I upvoted @mousio's answer, but I'd prefer to see the first of each line
type handled in its own template. So, in the spirit of TMTOWTDI, here's how I would have done it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:key name="byField2" match="line" use="field[@id=2]" />
<xsl:template match="/">
<document>
<xsl:apply-templates select="document/line" />
</document>
</xsl:template>
<xsl:template match="line[count(.|key('byField2', field[@id=2])[1])=1]">
<Message>
<ID><xsl:value-of select="field[@id=2]" /></ID>
<xsl:apply-templates select="key('byField2', field[@id=2])" mode="m">
<xsl:sort select="field[@id=3]" />
</xsl:apply-templates>
</Message>
</xsl:template>
<xsl:template match="line" mode="m">
<xsl:if test="position()=1">
<dateStart><xsl:value-of select="field[@id=3]" /></dateStart>
</xsl:if>
<xsl:if test="position()=last()">
<dateEnd><xsl:value-of select="field[@id=3]" /></dateEnd>
</xsl:if>
</xsl:template>
<xsl:template match="line" />
</xsl:stylesheet>
I think this is easier to read (and probably more efficient on large documents, since it doesn't abuse //
).
Upvotes: 2
Reputation: 243599
This transformation shows how to find the wanted minimum and maximum:
<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:apply-templates select="line">
<xsl:sort select="field[@id=3]"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="line">
<xsl:if test="position()=1">
Earliest:
<xsl:copy-of select="."/>
</xsl:if>
<xsl:if test="position()=last()">
Latest:
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<document>
<line id="0">
<field id="2">X111</field>
<field id="3">2011-03-31</field>
</line>
<line id="1">
<field id="2">X111</field>
<field id="3">2011-04-04</field>
</line>
<line id="2">
<field id="2">X111</field>
<field id="3">2011-04-02</field>
</line>
<line id="3">
<field id="2">X222</field>
<field id="3">2011-04-04</field>
</line>
<line id="4">
<field id="2">X222</field>
<field id="3">2011-04-01</field>
</line>
<line id="4">
<field id="2">X333</field>
<field id="3">2011-04-01</field>
</line>
</document>
produces:
Earliest:
<line id="0">
<field id="2">X111</field>
<field id="3">2011-03-31</field>
</line>
Latest:
<line id="3">
<field id="2">X222</field>
<field id="3">2011-04-04</field>
</line>
Explanation:
Choosing the first and last element from the sorted node-list. The dates are in a "good" format, so they are sorted just as strings.
Upvotes: 0
Reputation: 10337
This can probably be optimized but it returns the requested results:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="test" match="line" use="field[@id=2]"/>
<xsl:template match="/">
<document>
<xsl:for-each select="//line[generate-id()=generate-id(key('test',field[@id=2]))]">
<xsl:sort select="field[@id=2]"/>
<Message>
<ID>
<xsl:value-of select="field[@id=2]"/>
</ID>
<xsl:apply-templates select="key('test',field[@id=2])">
<xsl:sort select="field[@id=3]"/>
</xsl:apply-templates>
</Message>
</xsl:for-each>
</document>
</xsl:template>
<xsl:template match="line">
<xsl:if test="position()=1">
<dateStart>
<xsl:value-of select="field[@id=3]"/>
</dateStart>
</xsl:if>
<xsl:if test="position()=last()">
<dateEnd>
<xsl:value-of select="field[@id=3]"/>
</dateEnd>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Upvotes: 3