SoapUI property transfer and xpath/xquery number formatting - xpath

I have this property-transfer in SoapUI:
declare namespace soapEnv="http://schemas.xmlsoap.org/soap/envelope/";
//soapEnv:Body/LoginResponse/baseSequenceId
and lets say it returns 123456. But I want 123457 (what I get +1)
I tried this:
declare namespace soapEnv="http://schemas.xmlsoap.org/soap/envelope/";
//soapEnv:Body/LoginResponse/baseSequenceId + 1
but I get 123457.0 as a result. I tried some reformatting methods I found, but most possibly I did not use them in the correct way. I am quite new at this stuff.
I also tried this (with xquery):
declare namespace soapEnv="http://schemas.xmlsoap.org/soap/envelope/";
let $x := //soapEnv:Body/LoginResponse/baseSequenceId
return $x
and tried several things with $x but everything I tried ended up with null or InvocationTargetException.
Any help is appreciated !

Thanks a lot for your suggestions, although I couldn't make them work :(
Maybe there is something wrong with my SoapUI because all xpath functions return null..
I made it work with groovy:
groovyUtils = new com.eviware.soapui.support.GroovyUtils( context )
loginResponse = groovyUtils.getXmlHolder("Login#Response")
loginResponse.declareNamespace( "soapEnv", "http://schemas.xmlsoap.org/soap/envelope/" )
sessionIdStr = loginResponse.getNodeValue( "//soapEnv:Body/LoginResponse/sessionId" )
baseSequenceIdStr = loginResponse.getNodeValue( "//soapEnv:Body/LoginResponse/baseSequenceId" )
sequenceIdStr = (baseSequenceIdStr.toInteger() + 1).toString()
createRequest = groovyUtils.getXmlHolder("Create#Request")
createRequest.declareNamespace( "soapEnv", "http://schemas.xmlsoap.org/soap/envelope/" )
createRequest.setNodeValue( "//soapEnv:Header/SessionId", sessionIdStr )
createRequest.setNodeValue( "//soapEnv:Header/TransactionId", baseSequenceIdStr )
createRequest.setNodeValue( "//soapEnv:Header/SequenceId", sequenceIdStr )
createRequest.updateProperty()

Note that if the value of //soapEnv:Body/LoginResponse/baseSequenceId + 1 is an integer, XPath should not put in a decimal point when converting it to a string.
But maybe in this case XPath is returning a number, and it's SoapUI that's converting it to a string, and using a decimal point.
I would first try (updated):
string(//soapEnv:Body/LoginResponse/baseSequenceId + 1)
This is to force the conversion to string to happen within XPath, so that SoapUI won't have a chance to do anything funny with a numeric value.
Alternatively, you could try
floor(//soapEnv:Body/LoginResponse/baseSequenceId + 1)
or even
string(floor(...))
If that doesn't work, you could try
substring-before(//soapEnv:Body/LoginResponse/baseSequenceId + 1, '.')
It's not very elegant, but it might work.

Related

xquery parameter query

I'm trying to query an exist-db with xquery by taking parameters from the URL and building up seach parameters
xquery version "1.0";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare option exist:serialize "method=xml media-type=text/xml omit-xml-declaration=no indent=yes";
let $param1:= request:get-parameter("param1",'0')
let $person :=
if($param1 = '0')
then "'*'"
else concat('contributions/person/#val="',$param1,'"')
return
<xml>
{
for $x in subsequence(//foo/bar[$person],1,3)
return $x
}
</xml>
The code above shows that I get the parameter from the url $param1.
variable $person checks to see if there was a parameter and based on that creates a query parameter. This variable works fine, from testing it prints out either '*' for no param or
contributions/person/#val='hello, world'
When I run the query it prints out as if the value is '*'. In the for $x part, can I pass a variable like that? I've tried putting concat($person,'') with the same results. Hardcoding the full path gives me the results I'm looking for, but I'm looking to create something more dynamic.
To note: there is only one variable, $person, but there will be others once I get it to work
I think ideally you would avoid dynamic string evaluation. In this example, some pretty simple reorganization would solve the problem without it:
<xml>
{
for $x in subsequence(//foo/bar[
if ($param1 = '0')
then *
else (contributions/person/#val = $param1)
],1,3)
return $x
}
</xml>
However, you can use eval(), but keep in mind there are security risks:
<xml>
{
for $x in subsequence(eval(
concat('//foo/bar[',$person,']')
),1,3)
return $x
}
</xml>

How to get part of string after some word with Ruby?

I have a string containing a path:
/var/www/project/data/path/to/file.mp3
I need to get the substring starting with '/data' and delete all before it. So, I need to get only /data/path/to/file.mp3.
What would be the fastest solution?
'/var/www/project/data/path/to/file.mp3'.match(/\/data.*/)[0]
=> "/data/path/to/file.mp3"
could be as easy as:
string = '/var/www/project/data/path/to/file.mp3'
path = string[/\/data.*/]
puts path
=> /data/path/to/file.mp3
Using regular expression is a good way. Though I am not familiar with ruby, I think ruby should have some function like "substring()"(maybe another name in ruby).
Here is a demo by using javascript:
var str = "/var/www/project/data/path/to/file.mp3";
var startIndex = str.indexOf("/data");
var result = str.substring(startIndex );
And the link on jsfiddle demo
I think the code in ruby is similar, you can check the documentation. Hope it's helpful.
Please try this:
"/var/www/project/data/path/to/file.mp3".scan(/\/var\/www(\/.+)*/)
It should return you all occurrences.

Replace with use of regular expression result

Let's say I've got some text with a couple tags like this:
[twitter:jpunt]
I want to replace those into something like this:
#Jpunt
How could I do this in Ruby? I've been researching regular expressions for a couple of hours, just with a lot of frustration as a result. Anyone?
This should do the job:
initial = "[twitter:jpunt]"
link = initial.gsub(/\[twitter:(\w+)\]/i, '#\1')
It is one line code (click here to test this code) >>
output = input.gsub(/\[([^:]+):([^\]]+)\]/) {
'#' + $2.capitalize + '' }
The above code works with any tag name. If you want just twitter to be allowed, then go with modification:
output = input.gsub(/\[twitter:([^\]]+)\]/) {
'#' + $1.capitalize + '' }

Ruby Regular Expression: Setting $1 variable in a hash

Everything in this code works properly, except the contents of the $1 variable aren't being properly displayed. According to my tests, all the matching is being done properly, I am just having trouble figuring out how to actually output the contents of $1.
codeTags = {
/\[b\](.+?)\[\/b\]/m => "<strong>#{$1}</strong>",
/\[i\](.+?)\[\/i\]/m => "<em>#{$1}</em>"
}
regexp = Regexp.new(/(#{Regexp.union(codeTags.keys)})/)
message = (message).gsub(/#{regexp}/) do |match|
codeTags[codeTags.keys.select {|k| match =~ Regexp.new(k)}[0]]
end
return message.html_safe
Thank you!
As soon as you do this:
codeTags = {
/\[b\](.+?)\[\/b\]/m => "<strong>#{$1}</strong>",
/\[i\](.+?)\[\/i\]/m => "<em>#{$1}</em>"
}
The #{$1} bits in the values are interpolated using whatever happens to be in $1 at the time. The values will most likely be "<strong></strong>" and "<em></em>" and those aren't very useful.
And regexp is already a regular expression object so gsub(/#{regexp}/) should be just gsub(regexp). Similar things apply to the keys of codeTags, they're already regular expression objects so you don't need to Regexp.new(k).
I'd change the whole structure, you're overcomplicating things. Just something simple like this would be fine for only two replacements:
message = message.gsub(/\[b\](.*?)\[\/b\]/) { '<strong>' + $1 + '</strong>' }
message = message.gsub(/\[i\](.*?)\[\/i\]/) { '<em>' + $1 + '</em>' }
If you try to do it all at once you'll have problems with nesting in something like this:
message = 'Where [b]is[/b] pancakes [b]house [i]and[/i] more[/b] stuff?'
You'd end up having to use a recursive gsub and possibly some lambdas if you wanted to properly handle things like that with a single expression.
There are better things to spend your time on than trying to be clever on something like this.
Response to comments: If you have more bb-tags and some smilies to worry about and several messages per page then you should HTMLify each message when you create it. You could store only the HTML version or both HTML and BB-Code versions if you want the BB-Code stuff around for some reason. This way you'd only pay for the HTMLification once per message and producing your big lists would be nearly free.

setAttribute, onClick and cross browser compatibility

I have read a number of posts about this but none with any solid answer. Here is my code:
// button creation
onew = document.createElement('input');
onew.setAttribute("type", "button");
onew.setAttribute("value", "hosts");
onew.onclick = function(){fnDisplay_Computers("'" + alines[i] + "'"); }; // ie
onew.setAttribute("onclick", "fnDisplay_Computers('" + alines[i] + "')"); // mozilla
odiv.appendChild(onew);
Now, the setAttribute() method (with the mozilla comment) works fine in mozilla but only if it comes AFTER the line above it. So in other words it seems to just default to whichever gets set last. The .onclick method (with the ie comment) does not work in either case, am I using it incorrectly?
Either way I can't find a way to make this work at all in IE, let alone in both. I did change the function call when using the .onclick method and it worked fine using just a simple call to an alert function which is why I believe my syntax is incorrect.
Long story short, I can't get the onclick parameter to work consistently between IE/Mozilla.
-- Nicholas
onew.setAttribute("type", "button");
Never use setAttribute on HTML documents. IE gets it badly wrong in many cases, and the DOM-HTML properties are shorter, faster and easier to read:
onew.type= 'button';
onew.onclick = function(){fnDisplay_Computers("'" + alines[i] + "'"); }; // ie
What is ‘alines’? Why are you converting it to a string and surrounding it with single quotes? It looks like you are trying to do something heinous involving evaluating code in a string (which is what you're doing below in the ‘onew.setAttribute’ version). Evaluating JavaScript code in strings is almost always the Wrong Thing; avoid it like the plague. In the above case, IE should do the same as Firefox: it shouldn't work.
If ‘alines[i]’ is a string, I guess what you're trying to do is make it remember that string by constructing a code string that will evaluate in JavaScript to the original string. But:
"'" + alines[i] + "'"
is insufficient. What happens if ‘alines[i]’ has an apostrophe in, or a backslash?
'O'Reilly'
you've got a syntax error and possible security hole. Now, you could do something laborious and annoying like:
"'" + alines[i].split('\\').join('\\\\').split("'").join("\\'") + "'"
to try to escape the string, but it's ugly and won't work for other datatypes. You could even ask JavaScript to do it for you:
uneval(alines[i])
But not all objects can even be converted to evaluatable JavaScript source strings; basically the entire approach is doomed to failure.
The normal thing to do if you just want to have the onclick callback call a function with a parameter is to write the code in the straightforward way:
onew.onclick= function() {
fnDisplay_Computers(alines[i]);
};
Generally this will work and is what you want. There is, however, a slight wrinkle which you may have hit here, which could be what is confusing you into considering the wacky approach with the strings.
Namely, if ‘i’ in this case is the variable of an enclosing ‘for’ loop, the reference to ‘alines[i]’ won't do what you think it does. The ‘i’ will be accessed by the callback function when the click happens — which is after the loop has finished. At this point the ‘i’ variable will be left with whatever value it had at the end of the loop, so ‘alines[i]’ will always be the last element of ‘alines’, regardless of which ‘onew’ was clicked.
(See eg. How to fix closure problem in ActionScript 3 (AS3) for some discussion of this. It's one of the biggest causes of confusion with closures in both JavaScript and Python, and should really be fixed at a language level some day.)
You can get around the loop problem by encapsulating the closure in its own function, like this:
function callbackWithArgs(f, args) {
return function() { f.apply(window, args); }
}
// ...
onew.onclick= callbackWithArgs(fnDisplay_Computers, [alines[i]]);
And in a later version of JavaScript, you'll be able to say simply:
onew.onclick= fnDisplay_Computers.bind(window, alines[i]);
If you would like to be able to use ‘Function.bind()’ in browsers today, you can get an implementation from the Prototype framework, or just use:
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner,
args.length===0? arguments : arguments.length===0? args :
args.concat(Array.prototype.slice.call(arguments, 0))
);
};
};
}
I usually use something like:
onew.onclick = new Function("fnDisplay_Computers('" + alines[i] + "')");
this should work both in IE e Firefox.
Use the addEventListener() function with "click" for the type argument for Mozilla-based browsers, and attachEvent() function with "onclick" as the sEvent argument for IE; I find it best to use a try/catch statement, for example:
try {
onew.attachEvent("onclick", //For IE
function(){fnDisplay_Computers("'" + alines[i] + "'"); });
}
catch(e) {
onew.addEventListener("click", //For Mozilla-based browsers
function(){fnDisplay_Computers("'" + alines[i] + "'"); },
false);
}
I think #3 protesteth too much. In a lot of situations I'm building a table dynamically and need to pass parameters to the callback function. It isn't a typesafe issue since my variable parm is an integer index to the table row in question. Here's code with both a variable and fixed parameter that seems to be cross-browser compliant:
for (i = 0; i < arrTableData.length; i++) {
eleTR = objTable.insertRow(i + 1);
cell = eleTR.insertCell(0);
cell.width = "21";
var myElement = document.createElement('img');
myElement.setAttribute('src', 'images/button_down.gif');
myElement.setAttribute('alt', 'Move item down');
myElement.onclick = new Function('moveItem(' + i + ',0)');
cell.appendChild(myElement);
}

Resources