ColdFusion: do I need to use structKeyExists for every deep struct member?

Let's say I just parsed another XML document that is in response to an API request. I want to know if a value nested deep inside exists. If my API request worked, it will be in the same place every time. If my API request fails, the XML root is very different.

If I try <cfif structKeyExists(myStruct.level1.level2.level3, 'myTarget')>

a failed request api, I get a fatal error: Element LEVEL1.LEVEL2 is undefined in MYSTRUCT

.

Of course, I could try to depend on the XML root level telling me success or failure and not looking for the result if it fails, but ... forbid that solution, what should I do?

Do I need to check for each level of structure? How in:

<cfif structKeyExists(myStruct, 'level1') 
  and structKeyExists(myStruct.level1, 'level2') 
    and structKeyExists(myStruct.level1.level2, 'level3') 
      and structKeyExists(myStruct.level1.level2.level3, 'myTarget')>
<!--- ... --->
</cfif>

      

This is not a real world problem, it is just something that I have encountered too many times. Please do not tell me about solutions to change APIs or solutions like the ones in the third paragraph.

Thank!

edit: I should have mentioned why I can't use isDefined () - some of the keys don't have syntactically valid names, so isDefined () throws an error like myStruct.level1 [42] .level3

+2


source to share


5 answers


XMLSearch

I would use a parsed XML document (i.e. xmlDoc

) and XMLSearch :

<cfset xmlDoc = xmlParse(responseData)>
<cfset nodes = XmlSearch(xmlDoc, '/level1/level2/level3/myTarget')>
<cfif arrayLen(nodes)>
    <!--- do something, you have the "nodes" array to work with too --->
</cfif>

      

xpath for XMLSearch()

assumes the structural keys are nodes. You will need to change accordingly if for example "myTarget" is a node attribute.

StructFindKey



Another way to do it: StructFindKey .

<cfset result = structFindKey(myStruct, "myTarget")>
<cfif arrayLen(result) AND result.path EQ "level1.level2.level3">
    <!--- do something --->
</cfif>

      

Conclusion

Not tested, but I believe it will be faster than using IsDefined()

or try-catch block. Has an advantage over XMLValidate()

not needing a DTD. And even with a DTD, the node you want can be specified as optional, so it can still confirm.

+4


source


You can check the XML against the DTD to make sure the document is in the correct format. XmlParse()

and XmlValidate()

both take DTD as parameter.



<cfset validateResult = XmlValidate(myXmlDocument, myDTD)>
<cfif validateResult.status>
    <!--- xml is valid continue processing --->
<cfelse>
    <!--- xml did not validate handle the error --->
</cfif>

      

+3


source


Personally, I wouldn't be crazy to check every level of this "deep" structure. I would assume that if the top level exists, then the rest of the document will be as you expect, and I'll just post the document there.

If you'd like, you might try to eliminate the value in your structure and wrap it in try/catch

. This way you can handle any errors at any level at the same time.

<cftry>

  <cfset myVar = myStruct.level1.level2.level3 />

<cfcatch type="any">
  <!--- Handle error --->
</cfcatch>
</cftry>

      

Hope this helps.

+2


source


I know I will get a dash here, but here it isDefined()

can save you a lot of information:

<cfif isDefined(structKeyExists(myStruct.level1.level2.level3)>
<!--- do something --->
</cfif>

      

+2


source


I know this is a year, but I'm going to answer here. I struggled with this for a long time until I found a simple solution. If I know the XML structure already, a simple IsDefined works to check if a node or node attribute exists. I don't think most people know you can do this, or tried and failed because they did not include single quotes in the IsDefined function.

So say that I will grab some custom xml from a web service somewhere and want to display the user id.

<cfhttp url="https://mycompany.com/mywebservices/getusers" username="me" password="mysecret">
<cfset userXMLDoc = XMLParse(ToString(cfhttp.FileContent).trim())>

<cfif IsDefined('userXMLDoc.Data.Record.User.XmlAttributes.id')>
  <cfdump var="#userXMLDoc.Data.Record.User.XmlAttributes.id#">
<cfelse>
  <cfoutput>Failed: No User ID found</cfoutput>
</cfif>

      

+1


source







All Articles