We are using the Argus WS and I'm stuck at figuring out how to consume the GetUpdatedPricesInDateTimeRange method.
It returns a complex object type and I'm unable to access the element structure within the returned object.
Getting the error
Element <e.g. element name> is undefined in a Java object of type class org.tempuri.GetUpdatedPricesInDateTimeRangeResponseGetUpdatedPricesInDateTimeRangeResult.
Webservice is being invoked as follows:
<cfinvoke
webservice="http://www.argusmedia.com/ArgusWSVSTO/ArgusOnline.asmx?wsdl"
method="GetUpdatedPricesInDateTimeRange"
returnvariable="PricesResponse">
<cfinvokeargument name="authToken" value="#AuthToken#"/>
<cfinvokeargument name="fromDateTime" value="2017-03-01"/>
<cfinvokeargument name="toDateTime" value="2017-03-02"/>
<cfinvokeargument name="startId" value="0"/>
</cfinvoke>
Any pointers would be really helpful.
Edit 1: CFDump return variable
Edit 2: Based on Leigh's suggestion, this is what worked for me.
XML
PricesResponse.get_any()[2].getAsString()
The XML had namespaces in it, hence XML Search worked as follows:
XmlSearch(XMLDoc,"//*[local-name()='Element_Name']")
Based on Leigh's comment, get_any() method of the returnvariable can be used to get the underlying XML.
Dump of get_any() returns the array of Message Elements.
getAsString() method of get_any() returns the expected XML string.
<cfdump var="#PricesResponse.get_any()[1].getAsString()#">
Note : The intended XML can be in either of the MessageElement[] array element and not in the first one.
Note 2 : XMLSearch with namespaces in the XML worked as follows:
XmlSearch(XMLDoc,"//*[local-name()='Element_Name']")
Related
I recently moved my code from CF9 to CF11 and I am having issues when I am trying to use serializeJSON. According to CF docs:
Starting from ColdFusion 11, the data type is preserved during the
code execution time for Query and CFCs.
SerializeJSON considers datatypes defined in the database for
serialization. If the database defines a column as a string, any
number inserted into the column will still be treated as a string by
SerializeJSON.
But I guess this is not the case....
When I am pulling data out of a varchar column in CF9 it comes out like this "docid":"123" which is what I want but in CF11 the same data look like this "docid":123 and is causing an issue with what I am trying to do.
To be more specific, my ids look like this 2001101009460111385185 which is longer than what javascript can accept and they get converted into scientific notation. With the old format I didn't have this issue because my ids were treated as a string which is what I want.
Note: my code is exactly the same on both versions of CF
Anyone had this issue before and HOW did you go around it?
Code Sample
I am calling this function via an AJAX call, this function returns an array with a struct in it. When I dump the return value after I serialize the result I can see a JSON object in my console but the quotes are missing from all the number values. In a test file I created a simple query and then I am serializing the results and everything looks good......
<cffunction name="locationData" returnformat="json" access="remote">
<cfargument name="locationid" required="yes" type="string">
<cfargument name="clientBrandid" required="yes" type="string">
<cfscript>
locationData = new mod_sigweb.components.xamplifierCFCs.location_info();
result = locationData.getLocation(locationid,clientBrandid);
</cfscript>
<cfdump var="#serializeJSON(result[1],'struct')#">
<cfabort>
<cfreturn #result#>
</cffunction>
The unfortunate workaround that I have had to use for this bug is to concatenate a space to the end of the value
<cfloop index="i" from="1" to="#ArrayLen(result)#">
<cfset result[i].docid = result[i].docid & " "/>
</cfloop>
and then the js has to be aware of this (I know, it's bad) and remove that trailing space.
When you are calling a method of a webservice and want to omit an unrequired numeric variable that has a default value set coldfusion will throw the following error:
The fault returned when invoking the web service operation is:<br>
<pre>'' java.lang.IllegalArgumentException</pre>
Example:
<cfinvoke
webservice = "http://*.cfc?WSDL"
method="getFriendlyDay"
returnvariable="response"
refreshWSDL="true"
>
<cfinvokeargument name="dayNumber" omit="true"/>
</cfinvoke>
webservice component:
<cffunction name="getFriendlyDay" access="remote" returntype="any" output="no" description="get a friendly date from a number">
<cfargument name="dayNumber" type="numeric" required="no" default="0">
...
</cffunction>
My solution to this is to just not omit the argument. Pass in the default value. I just wanted to record this in case someone else gets the same error. Thus far it has only occurred on numeric values.
<cfinvoke
webservice = "http://*.cfc?WSDL"
method="getFriendlyDay"
returnvariable="response"
refreshWSDL="true"
>
<cfinvokeargument name="dayNumber" value="0" >
</cfinvoke>
Update:
I believe this probably relates to the bug outlined here:
The way Coldfusion handles optional arguments as a remote service is
that it allows the calling client to pass in a null value. In a
document/literal or rpc/encoded WSDL description, an element can
accept null unless it specifies "nillable='false'". The generated
WSDLs from Coldfusion do not use "nillable='false'" or "minOccurs='0'"
which instructs the client that they must include the parameter and
that it is ok to pass in a null value.
The problem however is that "numeric" or "boolean" argument types that
are optional will throw an "Illegal Argument Exception" when being
called by a client who is trying to explicitly pass in null ...
I have found a strange bug in Coldfusion when adding two different components into some form of data structure and dumping them out.
For the purpose of this question I have simplified my code. I have Component A and Component B, both have one property, one constructor, and one function.
I create an instance of A, and an instance of B. When I dump the instances out separately I see the correct metadata.
However when I add the two instances to a data structure (Component B in first slot, A in second), it appears as if the methods from the instance in slot 1 are duplicated in the instance in slot 2, even though they are different objects. i.e. I see CompBFunction, GetCompBProp, SetCompBProp listed as methods for Comp A. The properties however look correct.
It is worth mentioning, that I cannot call the methods for Comp B on Comp A - This seems to be a display issue. I tried calling CompBFunction on my object 'a' and it failed as expected.
Any help here would be greatly appreciated. I understand it's not a huge issue - but it can be rather misleading to see these methods here when they don't exist. There are many instances in my code where I would want to build up a Data Structure of mixed components.
Apologies if this question has been asked before - I searched high and low, but could not find any info on it. I am using CF9.
Here is my example code:-
Component A
component name="CompA" output="false" cache="false" accessors="true" {
property name="CompAprop" type="Numeric" getter="true" setter="true";
public CompA function init() output="false" {
return this;
}
public String function CompAFunction() output="false" {
return "All";
}
}
Component B
component name="CompB" output="false" cache="false" accessors="true" {
property name="CompBprop" type="Numeric" getter="true" setter="true";
public CompB function init() output="false" {
return this;
}
public String function CompBFunction() output="false" {
return "All";
}
}
index.cfm
<cfset a = new CompA() />
<cfset b = new CompB() />
<cfset test = ArrayNew(1) />
<cfset test[1] = b />
<cfset test[2] = a />
<cfdump var="#test#" /> <!--- METHODS FOR B APPEAR IN METADATA FOR A --->
<cfdump var="#test[1]#" /> <!--- METADATA CORRECT --->
<cfdump var="#test[2]#" /> <!--- METADATA CORRECT --->
<cfabort />
Images
It works fine for me on CF 9.0.1 (update: and on CF10), and I have never encountered anything like the problem you're describing in the past. People would have raised this as a bug if it was an actual problem with ColdFusion, because it'd crop up fairly frequently, I reckon.
You have noticed that you're putting CompB into the array at index 1, and CompA into element 2, yeah? This conflicts with what you say in your question "However when I add the two instances to a data structure (Component A in first slot, B in second),". So one would expect them to be the "wrong" way round?
Let's say i've just parsed someone else's XML document which is a 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 root of the XML is very different.
If I try <cfif structKeyExists(myStruct.level1.level2.level3, 'myTarget')> on a failed api request, I get the fatal error: Element LEVEL1.LEVEL2 is undefined in MYSTRUCT.
Of course, I could try to depend on the root level of the XML telling me of success or failure, and not looking for the result if it failed, but... barring that solution, what should i do?
Do i need to check for the existence of each level of the struct? As 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, this is just something i've faced too many times. Please don't tell me solutions that involve changing the API or solutions like those in the third paragraph.
Thanks!
edit: i should have mentioned why i can't use isDefined() - some of the keys do not have syntactically valid names, so isDefined() throws an error, eg myStruct.level1[42].level3
XMLSearch
I would use the 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 structure keys are nodes. You would need to modify accordingly if, for instance, 'myTarget' is an attribute of a node.
StructFindKey
Another way of doing this would be StructFindKey.
<cfset result = structFindKey(myStruct, "myTarget")>
<cfif arrayLen(result) AND result.path EQ "level1.level2.level3">
<!--- do something --->
</cfif>
Conclusion
Haven't tested, but I believe either will be faster than using IsDefined() or a try-catch block. Has the advantage over XMLValidate() of not needing a DTD. And, even with a DTD, the node you want may be defined as optional, so it could still validate.
You could validate the XML against a DTD to make sure the document was in the right format. XmlParse() and XmlValidate() both take a DTD as a parameter.
<cfset validateResult = XmlValidate(myXmlDocument, myDTD)>
<cfif validateResult.status>
<!--- xml is valid continue processing --->
<cfelse>
<!--- xml did not validate handle the error --->
</cfif>
Personally I wouldn't go crazy checking for every level of a 'deep' structure like this. I would presume that if the top level exists the rest of the document will be as you expect, and I'd just address the document from there.
If you wanted you could perhaps try to address the value in your struct and wrap it in a try/catch. That way you can handle any errors at any 'level' in the same way.
<cftry>
<cfset myVar = myStruct.level1.level2.level3 />
<cfcatch type="any">
<!--- Handle error --->
</cfcatch>
</cftry>
Hope that helps some.
I know I'm going to get booed off the stage here, but this is where isDefined() can save you a lot of typing:
<cfif isDefined(structKeyExists(myStruct.level1.level2.level3)>
<!--- do something --->
</cfif>
I know this is a year old, but I'm going to put in an answer here. I struggled for a good long time with this one, till I found a simple solution. If I know the structure of the XML already, a simple IsDefined works to test if the node or node attribute exists. I don't think most people know you can do this, or have tried and failed because they didn't include single quotes in the IsDefined function.
So say I grab some user xml from a web service somewhere and want to display the user's 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>
By default, ColdFusion passes simple types (like numeric, string, and GUID) by value to functions. I'd like to pass a simple type by reference.
I'm currently wrapping a simple value in a struct (they get passed by reference). This solves my problem but it is very ugly:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" type="struct">
<cfset OutVariable.ID = 5>
</cffunction>
<cfset OutVariable=StructNew()>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable.ID#</cfoutput>
I'd rather something like this:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" passbyref="true">
<cfset OutVariable = 5>
</cffunction>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable#</cfoutput>
AFAIK, there's no way to pass simple values by reference in ColdFusion. The only workaround I can think of is the one you're already using.
Instead, I would suggest trying to restructure your program to work with the grain of the language. In cases where there's only one simple value to "modify", you could just make your function return the new value, and call it like:
<cfset SomeVar = TheFunctionName(SomeVar)>
In cases where you're modifying multiple values, take a step back and think about whether it's possible to bundle those multiple values up into a CFC with your mutator functions becoming methods of the CFC. This could be clearer and more maintainable solution anyway.
You can arrange for the variables used outside and inside the function to be in a scope that exists in both code areas. For example, if you put a variable in the "session" or the "request" scope you will be able to access it from within the function. The changes made will persist.
Note that when you are doing this you aren't actually "passing" the variables to the function. The function just assumes the variable exists or creates it, depending on how you code it.
<cffunction name="TheFunctionName">
<cfset Request.StrVar = "inside function<br />" />
</cffunction>
<cfscript>
Request.StrVar = "outside function<br />";
WriteOutput(Request.StrVar);
TheFunctionName();
WriteOutput(Request.StrVar);
</cfscript>
About ColdFusion Scopes
If there is any doubt about the calling page declaring the variable in advance when it is required you'll have to do some legwork with the <cfparam> tag or IsDefined() function.
If you:
declare the function inside of a CFC
invoke the function using <cfinvoke>
You would be able to specify the <cfinvoke> parameter "returnvariable", and then output that variable however you like.
<cfinvoke component="this" method="TheFunctionName" returnvariable="blah">
<cfinvokeargument name="data" value="whatever" type="string">
<cfreturn data>
</cfinvoke>
<cfdump var="#blah#">
If you are writing everything in cfscript, then I would go with what SurroundedByFish said.