I chat to Andrew Myers a bit on Twitter (OK, well it goes out to the entire Twitter universe, but you know what I mean). And a few weeks ago he flicked through some interesting code to me. I've been meaning to write this up for three weeks now, but other stuff seems to keep cropping up that I "need" to write up first. Anyway, I've just looked at his repro case and it's an interesting one. And the code speaks for itself (thus making this a very easy article to write):
<p>It's okay to use a dot in a struct key, as long as that is the end of the line, eg.</p>
<cfset plutarch=StructNew()>
<cfset plutarch['lives.antony']="Antony's grandfather was the orator Antonius, who joined the party of Sulla...">
<cfset plutarch['lives.brutus']="Marcus Brutus was a descendant of that Junius Brutus whose bronze statue, with...">
<cfset plutarch['lives.caesar']="The wife of Caesar was Cornelia, the daughter of the Cinna...">
<cfset plutarch['lives.caesar.notes']=StructNew()>
<cfset plutarch['lives.caesar.notes'].one = "Many think that opening paragraphs of this Life, describing the birth and boyhood of Caesar, have been lost.">
<p>It seems all these keys work independently of each other - ie. a dot is a valid character in a struct key. But what happens now if we assign something to 'lives'?</p>
<cfdump var="#plutarch#">
<cfset plutarch['lives'] = "A series of biographies of famous Greeks and Romans, arranged in pairs to illuminate their common moral virtues and vices...">
<cfdump var="#plutarch#">
<p>Still good....</p>
<p>Or is it? What if what we want to assign is another struct?</p>
<cfset plutarch['lives'] = StructNew()>
<cfdump var="#plutarch#">
<p>Ooops!</p>
The "oops" is because this is what happens on ColdFusion (CF 9.0.2 and CF 10.0.9):
It's okay to use a dot in a struct key, as long as that is the end of the line, eg.
It seems all these keys work independently of each other - ie. a dot is a valid character in a struct key. But what happens now if we assign something to 'lives'?
struct | |||||
---|---|---|---|---|---|
lives.antony | Antony's grandfather was the orator Antonius, who joined the party of Sulla... | ||||
lives.brutus | Marcus Brutus was a descendant of that Junius Brutus whose bronze statue, with... | ||||
lives.caesar | The wife of Caesar was Cornelia, the daughter of the Cinna... | ||||
lives.caesar.notes |
|
struct | |||||
---|---|---|---|---|---|
lives | A series of biographies of famous Greeks and Romans, arranged in pairs to illuminate their common moral virtues and vices... | ||||
lives.antony | Antony's grandfather was the orator Antonius, who joined the party of Sulla... | ||||
lives.brutus | Marcus Brutus was a descendant of that Junius Brutus whose bronze statue, with... | ||||
lives.caesar | The wife of Caesar was Cornelia, the daughter of the Cinna... | ||||
lives.caesar.notes |
|
Still good....
Or is it? What if what we want to assign is another struct?
struct | |||
---|---|---|---|
lives |
| ||
lives.antony | undefined | ||
lives.brutus | undefined | ||
lives.caesar | undefined | ||
lives.caesar.notes | undefined |
Ooops!
Oops indeed. Where's all the data gone?
I also ran it on Railo 4.1 and OpenBD 3.0, and they both work fine. On a whim I also ran it on CF 8.0.1, and it runs fine on that too. Here's what should have happened (this is the CF8 output):
struct | |||||
---|---|---|---|---|---|
lives |
| ||||
lives.antony | Antony's grandfather was the orator Antonius, who joined the party of Sulla... | ||||
lives.brutus | Marcus Brutus was a descendant of that Junius Brutus whose bronze statue, with... | ||||
lives.caesar | The wife of Caesar was Cornelia, the daughter of the Cinna... | ||||
lives.caesar.notes |
|
This is the correct output.
I tried to find a bug for this, but drew a blank. I'll raise one now: 3539842.
Update
As tipped by Matt, the bug is with <cfdump>, not with struct support. I added this code to the bottom of the file, and its output follows:
So the values are there. It's just that <cfdump> must go about things a curious way when it comes to traverse the object it's dumping, and gets confused. So that's heartening. Mostly. Kinda.
As tipped by Matt, the bug is with <cfdump>, not with struct support. I added this code to the bottom of the file, and its output follows:
<cfloop item="topKey" collection="#plutarch#">
<cfoutput>
[#topKey#]:
<cfif isSimpleValue(plutarch[topKey])>
[#plutarch[topKey]#]<br />
<cfelse>
<cfloop item="innerKey" collection="#plutarch[topKey]#">
<cfoutput>
[#innerKey#]:
[#plutarch[topKey][innerKey]#]<br />
</cfoutput>
</cfloop>
</cfif>
</cfoutput>
</cfloop>
[lives.caesar.notes]: [ONE]: [Many think that opening paragraphs of this Life, describing the birth and boyhood of Caesar, have been lost.] [lives.antony]: [Antony's grandfather was the orator Antonius, who joined the party of Sulla...] [lives.caesar]: [The wife of Caesar was Cornelia, the daughter of the Cinna...] [lives.brutus]: [Marcus Brutus was a descendant of that Junius Brutus whose bronze statue, with...] [lives]:
So the values are there. It's just that <cfdump> must go about things a curious way when it comes to traverse the object it's dumping, and gets confused. So that's heartening. Mostly. Kinda.
Andrew, thanks for taking the time to write this up, and pass it on to me. The beer's on me next time we're in the same place at the same time.
Righto.
--
Adam