Copy link to clipboard
Copied
I'm working on a project that involves using PDF forms and I am trying to dynamically grab an element in the pdf form. I use StructFindKey to find the form element. Then I tried using StructGet to specifically grab the element using the path provided by StructFindKey. But what I'm seeing is that StructGet isn't finding the element whereas if I use Evaluate I do get the element. Instead StructGet is essentially overwriting the element and creating a new empty structure for its value. (The element is an array of values, so it's easy to see that StructGet is replacing it.) This is using CF 8.
<cfpdfform action="read" source="#request.path.file##attributes.fileid#.pdf" result="frmStruct"></cfpdfform>
<cfset tst = StructFindKey(frmStruct,"EF153", "all")>
<cfdump var="#tst#">
<cfset tst3 = Evaluate("frmStruct#tst[1].path#")>
<cfdump var="#tst3#">
<cfset tst2 = StructGet("frmStruct#tst[1].path#")>
<cfdump var="#tst2#">
Am I just misunderstanding what StructGet does?
Copy link to clipboard
Copied
Hi SteveTX,
Well. If you look at the livedocs here,
http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=functions_s_23.html
It clearly says that the "StructGet" function creates a structure (or) structures which means that we can use it for extracting any element. Also in the same document page, if you go through the "Returns" and "Usage" titles you will get to know that it has the ability to create structures to make the "pathdesired" variable as a valid path.
HTH
Copy link to clipboard
Copied
I understood that StructGet would create a value if it doesn't exist, but in this case it is overwriting an existing value. For example, in my test the initial StructFindKey is returning the following path:
.s_316.pg1.StatementTable.EF153
If I do StructGet("frmStruct.s_316.pg1.StatementTable"), I get back the StatementTable structure which has three elements, one of which is the array EF153. But when I do StructGet("frmStruct.s_316.pg1.StatementTable.EF153") I don't get that array; I get an empty Structure and EF153 in original has been changed. I guess it's just that StructGet can only return structures and turns the element into a structure if it isn't one. Thanks for the response.
Copy link to clipboard
Copied
Do you need to access the relevant element by its dotted path? structFindValue() does provide the "owner" reference for you, which is the relevant element. Why not just reference it that way?
I've never used structGet() (never found a need to), but it behaves rather strangely to me. Look at this code:
<cfset st = {
outer = {
mid = {
inner1 = "value",
inner2 = {a=1,b=2}
}
}
}>
<cfdump var="#st#" label="st">
<hr /><cfset theWholeStruct = structGet("st")>
<cfdump var="#theWholeStruct#" label="theWholeStruct">
<cfdump var="#st#" label="st">
<hr /><cfset theSubStruct = structGet("st.outer.mid")>
<cfdump var="#theSubStruct#" label="theSubStruct">
<cfdump var="#st#" label="st">
<hr /><cfset inner1Value = structGet("st.outer.mid.inner1")><!--- this overwrites inner1 --->
<cfdump var="#inner1Value#" label="inner1Value">
<cfdump var="#st#" label="st">
<hr /><cfset inner2Value = structGet("st.outer.mid.inner2")>
<cfdump var="#inner2Value#" label="inner2Value">
<cfdump var="#st#" label="st">
<hr /><cfset inner3Value = structGet("st.outer.mid.inner3")><!--- this CREATES inner3 --->
<cfdump var="#inner3Value#" label="inner3Value">
<cfdump var="#st#" label="st">
<hr />
(it's nice how - on a CF forum - there is no option to apply CFML or even HTML source highlighting!)
It all works fine until where I indicate. I'm seeing the same as you were: it's blowing away the value at that key and replacing it with an empty struct. However the similar subsequent call - which attempts to get a struct rather than a simple value - works fine.
So I would say there are two bugs here:
* structGet() should not overwrite existing values just because they're not structs
* structGet() should not create anything. It's structGET(), it's not structGETIFITSTHEREOTHERWISECREATEITANDGETIT()
Obviously the second point is "documented", but it's dumb behaviour.
I can't see how the first is anything other than a bug.
--
Adam
Copy link to clipboard
Copied
I'm inclined to agree with you that it's buggy behavior. I'm using the owner element from StructFindKey as you suggested and that is working out for me. Thanks for your response.
Copy link to clipboard
Copied
How is it a bug that it blows something away? If you have a variable, foo, that was a string, and then said foo = arrayNew(1), you just blew away the original value... but you did it on purpose. structGet is the same.
As for uses - I've found one use for it. If you want to write code that can work with any scope, let's say session or application or server, you can use structGet to return a point.
cfset ptr = structGet(desiredscope)
I use this technique in my scopeCache custom tag.
It is also useful for making a shorthand notation to a very long dynamic path.
Copy link to clipboard
Copied
How is it a bug that it blows something away? If you have a variable, foo, that was a string, and then said foo = arrayNew(1), you just blew away the original value... but you did it on purpose. structGet is the same.
Come on Ray. That's specious even for a CF apologist!
That a function named structGet() makes changes to the data it's getting from at all is a crap way of going about things. Something that "gets" should not be making changes. Still: it's documented as doing so, so - whilst it was a dumb thing to do - we should expect this. Fair enough. I wish I had noticed this during the CFMX prerelease so I could've... um... "questioned it vigorously" shall we say 😉
However what is wrong - and not documented - is that it will blow-away non-struct values at the target location. The docs say this:
An alias to the variable in the pathDesired parameter. If necessary, StructGet creates structures or arrays to make pathDesired a valid variable "path."
(my emphasis)
In the case where the key is not a struct, the variable "path" is still valid... it's just what's on that "path" isn't a struct. So structGet() - a function that's designed to get things from a struct - should just return either:
a) the value at that path, whatever it is;
b) nothing (not exactly the CF way, but makes more sense than what it currently does);
c) error with "that ain't a struct".
(in order of what would make most sense)
There is a gulf of difference between creating data where there was none before and removing valid data because the function's been implemented poorly / nonsensically.
Your arrayNew() analogy - such as it is - is obtuse. arrayNew() is for creating arrays. structGet() is for getting data from a struct. It's not for creating or overwriting data: that is a vagary of its implementation. Equally, arrayNew() always creates an array. structGet() will either return the data if it "likes" it, or - if the data is not to its liking - blow it away and replace it with something that is does "like".
There is no way that is desirable (or even remotely sensible) behaviour.
--
Adam
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Fracking email got snipped AGAIN. Sorry. Here is what I said:
Copy link to clipboard
Copied
> That a function named struct*+Get+*() makes changes to the data it's getting from +at all+ is a crap way of going about things. Something that "gets" should not be making changes. Still: it's documented as doing so, so - whilst it was a dumb thing to do - we should expect this. Fair enough. I wish I had noticed this during the CFMX prerelease so I could've... um... "questioned it vigorously" shall we say 😉So it sounds like you would be cool with it if it was just _named_
clearer?
Yep. As I alluded to earlier in the thread. Something called structGet() should get something from a struct. And that's it. Not least of all because if someone looks at the function without looking up the vagaries of its imlpementation in the docs, one could not possibly intuit its actual behaviour. This is a sign of poor function naming (or poor function implementation). What they've implemented is more like structGetItIfItIsThereAndItIsAStructAlreadyOtherwiseWipeWhatIsThereReplaceItWithAnEmptyStructAndReturnThat(). And this demonstrates another rule of thumb I have: if to describe what your function does requires a name like that: the function is doing too much.
If it was structSet(), I'd completely agree with its behaviour (although it's debateable as to whether something called structSet() should return anything).
Or structParam() (analogous to <cfparam>).
Basically its behaviour was correct prior to CFMX (well: inferring from the CF9 docs, anyhow).
I think what we disagree with here is what is valid. To me, the
existing data wasn't valid in terms of what structGet wants.
Sure. So it should error. Fine.
Or better: reframe what it considers valid. It's designed to get a value from a struct. However it only works if that value is also a struct. That's dumb.
What you seem to want is something like getSubstructFromStruct() (thus necessitating getStringFromStruct(), getArrayFromStruct(), getXmlFromStruct() etc too). Which is dumb. Why not simply do what it's told: get the thing - whatever it is - from the struct.
Without already knowing the docs, would you not expect a function called structGet() which takes a path to a key within a struct to just return whatever is on that path? Without this addition "functionality"?
--
Adam
Copy link to clipboard
Copied
Copy link to clipboard
Copied
God dang it! Please note I am NOT sending empty replies. It's the forum software.
I don't know if the Law of Backwards Compat would allow Adobe to ever
change it though.
Copy link to clipboard
Copied
I don't know if the Law of Backwards Compat would allow Adobe to ever
change it though.
Well I'll bring it up next time I get to talk to someone from Adobe about such things. Which should be... before too long... I think..?
And, yeah, good point re adding something to livedocs. Given I've been the on banging on about it so much, I'll chuck something up there. Soon.
--
Adam