Subject: Re: [xsl] The notion of Inheritance? From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx> Date: Fri, 30 Mar 2001 10:00:11 +0100 |
Hi William, > Now lets add a couple of wrinkles to it... Oh good ;) > 1. Suppose I'm using person[id = 1] as the default, but now > person[id = 2] (or any person for that matter) can contain nodes > that are not in the default. I want all the nodes of person[id = 2], > plus the nodes from person[id = 1] (I guess this is the union in > mathematical terms). OK. The solution I gave you works through the default structure (person[id = 1]) and the override structure (person[id = 2]) side by side. The normal processing is working through the default structure (by applying templates to the elements in 'inherit' mode), and the override structure is being worked through by passing elements as parameters. In the 'inherit' templates then the current node is an element in the default structure (e.g. person[id = 1]/first-name) and the $override parameter is the same-named element in the override structure (e.g. person[id = 2]/first-name). When you have one of the 'wrapper' elements (ones that only have element children, like 'person' or 'physical'), then you can go through the children of the overriding element and copy any that don't have a corresponding element in the children of the default element: <xsl:template match="*[*]" mode="inherit"> <xsl:param name="override" /> <xsl:variable name="default" select="." /> <xsl:copy> <xsl:for-each select="$default/*"> <xsl:apply-templates select="." mode="inherit"> <xsl:with-param name="override" select="$override/*[name() = name(current())]" /> </xsl:apply-templates> </xsl:for-each> <xsl:for-each select="$override/*"> <xsl:if test="not($default/*[name() = name(current())])"> <xsl:copy-of select="current()" /> </xsl:if> </xsl:for-each> </xsl:copy> </xsl:template> One thing about this is that it does change the ordering of the elements - all the elements that are new in the overriding structure will be added at the bottom of the default structure. No doubt you will return asking how to fix this, but it's fairly complicated, so I'd avoid needing it if you can ;) > 2. Suppose I add an <address> node that looks like this: [snip] > and I want to be able to get the defaults for only that persons' > country (which must be specified). This is looking (and feeling) > more and more like a Relational DB, I guess, but is this possible? Almost everything is *possible*. Again, this is a matter of expanding the template above. You only want to apply templates to the address element under the person element if the $override element's child address element's child country element has the same value as the default address element's child country element. So you need: <xsl:template match="*[*]" mode="inherit"> <xsl:param name="override" /> <xsl:variable name="default" select="." /> <xsl:copy> <xsl:for-each select="$default/*"> <xsl:if test="not(self::address) or country = $override/address/country"> <xsl:apply-templates select="." mode="inherit"> <xsl:with-param name="override" select="$override/*[name() = name(current())]" /> </xsl:apply-templates> </xsl:if> </xsl:for-each> <xsl:for-each select="$override/*"> <xsl:if test="not($default/*[name() = name(current())])"> <xsl:copy-of select="current()" /> </xsl:if> </xsl:for-each> </xsl:copy> </xsl:template> *Or* you could leave the template as it is, and instead have a template specifically for the address element, so that it only copies itself if the country matches: <xsl:template match="address" mode="inherit"> <xsl:param name="override" /> <xsl:variable name="default" select="." /> <xsl:if test="country = $override/country"> <xsl:copy> <xsl:for-each select="$default/*"> <xsl:apply-templates select="." mode="inherit"> <xsl:with-param name="override" select="$override/*[name() = name(current())]" /> </xsl:apply-templates> </xsl:for-each> <xsl:for-each select="$override/*"> <xsl:if test="not($default/*[name() = name(current())])"> <xsl:copy-of select="current()" /> </xsl:if> </xsl:for-each> </xsl:copy> </xsl:if> </xsl:template> > Or is there a better way to structure the XML in the first place? I > don't even know if a DTD will allow this sort of thing. Things are a lot easier if *one* of the default or the override has *the* structure that you want. Given the latter complication, it makes more sense if this is the child. So, something like (just taking the sample XML that you first posted): <people> <person> <id>1</id> <first-name>John</first-name> <last-name>Smith</last-name> <physical> <eyes>brown</eyes> <hair>black</hair> <height>6'1"</height> </physical> <occupation>Software Developer</occupation> </person> <person> <id>2</id> <first-name>Mary</first-name> <last-name>Jacobs</last-name> <physical> <eyes /> <hair /> <height /> </physical> <occupation>Welder</occupation> </person> <person> <id>3</id> <first-name>Joe</first-name> <last-name /> <physical> <eyes>blue</eyes> <hair /> <weight /> </physical> <occupation /> </person> </people> With this, you just need to have three templates: one that deals with elements with element children by copying the element and moving on to the content: <xsl:template match="*[*]"> <xsl:copy><xsl:apply-templates select="*" /></xsl:copy> </xsl:template> One that deals with elements with text inside them by copying them: <xsl:template match="*[text()]"> <xsl:copy-of select="." /> </xsl:template> And finally one that deals with elements that are empty by referring to the default and copying the relevant element from that default. If all your elements are named differently, this is really easy. I'd set up the default structure in a variable so that it can be accessed anywhere: <xsl:variable name="default" select="/people/person[id = 1]" /> Then it's just a matter of finding the descendant of the $default element with the relevant name: <xsl:template match="*[not(node())]"> <xsl:copy-of select="$default//*[name() = name(current())][1]" /> </xsl:template> (You might be able to make this slightly more efficient with a key.) This is getting very long so I'll stop now. I hope you'll follow up if you have questions. Cheers, Jeni --- Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
RE: [xsl] The notion of Inheritance, William Bagby | Thread | [xsl] Sorting list of XML data into, Tim Watts |
AW: [xsl] !!!showing HTML data in X, Marcus Klinge | Date | Re: [xsl] Does a transform understa, Francis Norton |
Month |