Search code examples
coldfusiongoogle-admin-sdkgoogle-directory-api

Variable dump shows data but returns as undefined


Hopefully this is a "DOH" moment but I can't figure out why this is happening. I am using a service object to get the members of a group in Google's Directory API. After creating the service object I used the following lines to test

<cfset themembers = groupservice.members().list("[My Group Key]").execute()>
<cfdump var="#themembers#">
<cfoutput>
   Isnull? #isNull(themembers.etag)#<br />
   SKExists? #structKeyExists(themembers,"etag")#<br />
   Is Defined? #isDefined("themembers.etag")#
</cfoutput>

The resulting output of the code looks like this screenshot

Looking at the dump I can see there is data in the etag field as well as an array of members. However, when I tried to output the key value I was getting an undefined error. I wrote a test output line and it's showing as NULL and not defined while the key DOES exist.

How is this possible when the dump of the variable clearly shows data? What am I missing here?

@Leigh

<cfdump var="#groupservice.members()#">
<cfdump var="#groupservice.members().list('[groupkey]')#">

Here are the dumps from the 2 lines above. The list method simply returns the groupkey name that i'm passing in - it's only after I run the execute method do I actually get the list of members (output in first image) but just like with the etag - it tells me members is undefined. sshot1


Solution

  • Short answer:

    Apparently this falls under the general rule of "Never trust CFDump. Sometimes it lies." Case in point, the variable themembers is not really a structure. It is an instance of the Members class. Instead of using dot notation, try invoking the relevant methods of the class:

    <cfset eTag = themembers.getETag()>
    <cfset membersArray = themembers.getMembers()>
    ..
    

    Longer Answer:

    While CFDump is a good debugging tool, it tends to favor "pretty presentation" over accuracy, so you cannot always trust what you see. The reason it displays themembers as a "struct" is because ColdFusion automatically matches Java types to ColdFusion types. If you take a look at the class hierarchy of Members, you can see it implements java.util.Map (via AbstractMap), which is automatically matched to a CF structure. Hence why CFDump (misleadingly) displays it as one.

    - com.google.api.services.admin.directory.model.Members
    - com.google.api.client.json.GenericJson
    - com.google.api.client.util.GenericData
    - java.util.AbstractMap   
    - java.lang.Object
    

    Although java objects are similar to structures, in some respects, they are not the same thing. So the results from certain functions may differ from what you would normally expect for structures, which is what happened here. i.e. You can view some of the property names, like you can with structure keys, but you cannot access the key/property values directly.

    That said, technically you should have been able to access the "eTag" and "members" properties with dot notation. The reason being that CF applies a bit of syntactic sugar to certain java objects to allow direct access of properties using dot notation. Just as if they were regular structure keys.

    ColdFusion can automatically invoke get_PropertyName_() and set_PropertyName_(value) methods if a Java class conforms to the JavaBeans pattern. As a result, you can set or get the property by referencing it directly, without having to explicitly invoke a method.

    However, as the docs note later on, there are some exceptions. This class appears to be one of them. So you will need to call the appropriate getPropertyName() methods explicitly. Ultimately, that is probably the best practice anyway, given that direct access does not always work.

    Example:

    // Simulate Members object 
    obj = createObject("java", "com.google.api.services.admin.directory.model.Members").init();
    // Initialize property values
    obj.setEtag("If you are seeing this text, it worked.");
    obj.setMembers( [] );
    
    writeOutput("<br>IsNull? "& isNull(obj.etag));
    writeOutput("<br>Key List: "& structKeyList(obj));
    writeOutput("<br>Exists? "& structKeyExists(obj, "etag"));
    writeOutput("<br>IsDefined? "& IsDefined("obj.etag"));
    writeOutput("<br>getETag(): "& obj.getETag());
    writeDump(var=obj.getMembers(), label="getMembers()");
    

    Results:

    IsNull? YES
    Key List: etag,members
    Exists? YES
    IsDefined? NO
    getETag(): If you are seeing this text, it worked.
    getMembers(): (empty array)