I'm working with a web service that returns the output below. I'm having a hard time building the XMLTYPE variable from the output. I get the following error when trying to output the CreateUserSessionFromInstanceResult
ORA-30625: method dispatch on NULL SELF argument is disallowed
procedure xmltest is
str_xml varchar2(32000);
v_xml XMLTYPE;
BEGIN
str_xml :='<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<CreateUserSessionFromInstanceResponse xmlns="http://archer-tech.com/webservices/">
<CreateUserSessionFromInstanceResult>4FFABEE05C4910A31FEC75D5FEDCDFB5</CreateUserSessionFromInstanceResult>
</CreateUserSessionFromInstanceResponse>
</soap:Body>
</soap:Envelope>';
v_xml := XMLTYPE(str_xml);
HTP.P(v_xml.extract('//CreateUserSessionFromInstanceResult/text()').getstringval());
END;
The error is on this line
HTP.P(v_xml.extract('//CreateUserSessionFromInstanceResult/text()').getstringval());
You're getting an error because your XPath expression
//CreateUserSessionFromInstanceResult/text()
matches nothing, and so extract(...) returns NULL. Of course, you can't call getstringval() on NULL, hence the error you see.
The reason your XPath isn't matching is because of namespaces. The xmlns attribute on the <CreateUserSessionFromInstanceResponse> element sets the namespace of this element and of the <CreateUserSessionFromInstanceResult> element it contains to be "http://archer-tech.com/webservices/". On the other hand, your XPath expression is searching for the element with the name CreateUserSessionFromInstanceResult in the default namespace, "".
The fix is to add a third parameter to extract that declares this namespace. You do this in the same way as it appears in the XML:
HTP.P(v_xml.extract('//CreateUserSessionFromInstanceResult/text()',
'xmlns="http://archer-tech.com/webservices/"').getstringval());
I came across a similar case and adding the extra parameter didn't work for me.
For me, the solution was to check if node exists and do a null check before using getstringval, like this:
if v_xml.existsnode('//CreateUserSessionFromInstanceResult', 'xmlns="http://archer-tech.com/webservices/"') > 0 and v_xml.extract('//CreateUserSessionFromInstanceResult/text()', 'xmlns="http://archer-tech.com/webservices/"') is not null then
HTP.P(v_xml.extract('//CreateUserSessionFromInstanceResult/text()', 'xmlns="http://archer-tech.com/webservices/"').getstringval());
end if;
Doing only the null check is probably enough.
Related
<?xml version="1.0" encoding="UTF-8"?>
<IDMResponse xmlns="http://www.nrf-arts.org/IXRetail/namespace/" MajorVersion="1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ARTSHeader>
<MessageID>1</MessageID>
<Response ResponseCode="Ok">
<RequestID>1</RequestID>
</Response>
</ARTSHeader>
</IDMResponse>
I tried
//[local-name() = 'Response']
and
#//*[local-name() = 'Response']
In stead of
//[local-name() = 'Response']
use
//*[local-name() = 'Response']
The predicate ([]) needs something to filter on.
// selects nothing. It tells to look for any descendant something. But it needs at least a following attribute or node to know what to do.
/* selects the root-node independend of name of element
//* selects all element-nodes independend of name
//# selects all attributes independend of name
//Response selects all elements with the name Response that are not in a namespace
//*[local-name()='Response'] selects all elements with the name Response independend off a namespace
//*[local-name()='Response' and #ResponseCode='Ok'] selects all elements with the name Response(independend off a namespace) and that have a attribute ResponseCode with the value Ok
Here are some more examples.
And take a look at this answer
To access the <Response ResponseCode="Ok"> node you can use
//Response
To extract the Ok value from there this should work:
//Response/#ResponseCode
Preamble
I have a JMeter script with an XPath Extractor, in this I have specified a query that gets multiple values from the XML document. This all works fine
XML
<?xml version="1.0" encoding="UTF-8"?>
<InventoryAvailabilityAdvice>
<Warehouse>WFC2</Warehouse>
<Timestamp>2019-07-31T23:00:02.177</Timestamp>
<InventoryItem>
<ItemNumber>80903</ItemNumber>
<AvailableQuantity UnitOfMeasure="EA">13</AvailableQuantity>
</InventoryItem>
<InventoryItem>
<ItemNumber>80901</ItemNumber>
<AvailableQuantity UnitOfMeasure="EA">17</AvailableQuantity>
</InventoryItem>
</InventoryAvailabilityAdvice>
Problem
When I then try to get these values in a loop using a JSR232 Sampler they don't seem to come out in the order declared in the XPath Query.
I expected theData_2 to contain the UnitOfMeasure attribute and theData_3 to contain the quantity, but as you can see they are reversed.
Question
Is this expected behavior? If so, when an element has multiple attributes how do I know which order those will be made available as?
Thanks
The order of XPath nodesets produced by union operator is not guaranteed, you can see putValuesForXPathInList() function for implementation details
Actually if you've decided to go for Groovy - you don't even need the XPath Extractor, you can use XmlSlurper class for parsing the XML response.
Example code:
def response = new XmlSlurper().parseText(prev.getResponseDataAsString())
response.InventoryItem.eachWithIndex { item, index ->
log.info('Item: ' + index)
log.info('ItemNumber: ' + item.ItemNumber)
log.info('AvailableQuantiry: ' + item.AvailableQuantity)
log.info('UnitOfMeasure:' + item.AvailableQuantity.#UnitOfMeasure)
}
Demo:
References:
Groovy: Processing XML
Apache Groovy - Why and How You Should Use It
Short Version
You do it in .NET with:
XmlNode.SelectNodes(query, selectionNamespaces);
Can you do it in javascript?
https://jsfiddle.net/9ctoxbh0/
Can you do it in msxml?
Attempt A:
IXMLDOMNode.selectNodes(query); //no namespaces option
Attempt B:
IXMLDOMNode.ownerDocument.setProperty("SelectionNamespaces", selectionNamespaces);
IXMLDOMNode.selectNodes(query); //doesn't work
Attempt C:
IXMLDOMDocument3 doc;
doc.setProperty("SelectionNamespaces", selectionNamespaces);
IXMLDOMNodeList list = doc.selectNodes(...)[0].selectNodes(query); //doesn't work
Long Version
Given an IXMLDOMNode containing a fragment of xml:
<row>
<cell>a</cell>
<cell>b</cell>
<cell>c</cell>
</row>
We can use the IXMLDOMNode.selectNodes method to select child elements:
IXMLDOMNode row = //...xml above
IXMLDOMNodeList cells = row.selectNodes("/row/cell");
and that will return an IXMLDOMNodeList:
<cell>a</cell>
<cell>b</cell>
<cell>c</cell>
And that's fine.
But namespaces break it
If the XML fragment originated from a document with a namespace, e.g.:
<row xmlns:ss="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<cell>a</cell>
<cell>b</cell>
<cell>c</cell>
</row>
The same XPath query will nothing, because the elements row and cell do not exist; they are in another namespace.
Querying documents with default namespace
If you had a full IXMLDOMDocument, you would use the setProperty method to set a selection namespace:
a
b
c
You would query the default namespace by giving it a name, e.g.:
Before: xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
After: xmlns:peanut="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
and then you can query it:
IXMLDOMDocument3 doc = //...document xml above
doc.setProperty("SelectionNamespaces", "xmlns:peanut="http://schemas.openxmlformats.org/spreadsheetml/2006/main");
IXMLDOMNodeList cells = doc.selectNodes("/peanut:row/peanut:cell");
and you get your cells:
<cell>a</cell>
<cell>b</cell>
<cell>c</cell>
But that doesn't work for a node
An IXMLDOMNode has a method to perform XPath queries:
selectNodes Method
Applies the specified pattern-matching operation to this node's context and returns the list of matching nodes as IXMLDOMNodeList.
HRESULT selectNodes(
BSTR expression,
IXMLDOMNodeList **resultList);
Remarks
For more information about using the selectNodes method with namespaces, see the setProperty Method topic.
But there's no way to specify Selection Namespaces when issuing an XPath query against a DOM Node.
How can I specify a namespace when querying nodes with XPath?
.NET Solution
.NET's XmlNode provides a SelectNodes method that provides accepts a XmlNamespaceManager parameter:
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("peanut", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
cells = row.SelectNodes("/peanut:row/peanut:cell", ns);
But i'm not in C# (nor am i in Javascript). What's the native msxml6 equivalent?
Edit: Me not so much with the Javascript (jsFiddle)
Complete Minimal Example
program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, msxml, ActiveX;
procedure Main;
var
s: string;
doc: DOMDocument60;
rows: IXMLDOMNodeList;
row: IXMLDOMElement;
cells: IXMLDOMNodeList;
begin
s :=
'<?xml version="1.0" encoding="UTF-16" standalone="yes"?>'+#13#10+
'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'+#13#10+
'<row>'+#13#10+
' <cell>a</cell>'+#13#10+
' <cell>b</cell>'+#13#10+
' <cell>c</cell>'+#13#10+
'</row>'+#13#10+
'</worksheet>';
doc := CoDOMDocument60.Create;
doc.loadXML(s);
if doc.parseError.errorCode <> 0 then
raise Exception.CreateFmt('Parse error: %s', [doc.parseError.reason]);
doc.setProperty('SelectionNamespaces', 'xmlns:ss="http://schemas.openxmlformats.org/spreadsheetml/2006/main"');
//Query for all the rows
rows := doc.selectNodes('/ss:worksheet/ss:row');
if rows.length = 0 then
raise Exception.Create('Could not find any rows');
//Do stuff with the first row
row := rows[0] as IXMLDOMElement;
//Get the cells in the row
(row.ownerDocument as IXMLDOMDocument3).setProperty('SelectionNamespaces', 'xmlns:ss="http://schemas.openxmlformats.org/spreadsheetml/2006/main"');
cells := row.selectNodes('/ss:row/ss:cell');
if cells.length <> 3 then
raise Exception.CreateFmt('Did not find 3 cells in the first row (%d)', [cells.length]);
end;
begin
try
CoInitialize(nil);
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
This is answered on MSDN:
How To Specify Namespace when Querying the DOM with XPath
Update:
Note, however, that in your second example XML, the <row> and <cell> elements are NOT in the namespace being queried by the XPath when adding xmlns:peanut to the SelectionNamespaces property. That is why the <cell> elements are not being found.
To put them into the namespace properly, you would have to either:
change the namespace declaration to use xmlns= instead of xmlns:ss=:
<row xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<cell>a</cell>
<cell>b</cell>
<cell>c</cell>
</row>
use <ss:row> and <ss:cell> instead of <row> and <cell>:
<ss:row xmlns:ss="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<ss:cell>a</cell>
<ss:cell>b</cell>
<ss:cell>c</cell>
</ss:row>
The SelectionNamespaces property does not magically put elements into a namespace for you, it only specifies which namespaces are available for the XPath query to use. The XML itself has to put elements into the proper namespaces as needed.
Update:
In your new example, cells := row.selectNodes('/ss:row/ss:cell'); does not work because the XPath query is using an absolute path, where the leading / starts at the document root, and there are no <row> elements at the top of the XML document, only a <worksheet> element. That is why rows := doc.selectNodes('/ss:worksheet/ss:row'); works.
If you want to perform an XPath query that begins at the node being queried, don't use an absolute path, use a relative path instead:
cells := row.selectNodes('ss:row/ss:cell');
Or simply:
cells := row.selectNodes('ss:cell');
Is it possible to check two or more variables when mocking a response using SoapUi?
Here's a sample of the request:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:crm="http://www.example.com/project/wsdl/crm/">
<soap:Body>
<crm:SearchCustomer>
<FirstName>Francis</FirstName>
<LastName>Zabala</LastName>
</crm:SearchCustomer>
</soap:Body>
</soap:Envelope>
Here's the Extract Xpath
declare namespace crm='http://www.example.com/project/wsdl/crm/';
declare namespace soap='http://www.w3.org/2003/05/soap-envelope';
//crm:SearchCustomer[1]/FirstName
Expected value:
Francis
What about if I also want to check the last name?
You can try using the follow XPath expression which check for the text content of the <FirstName> and <LastName> and use boolean() XPath function to check if the expression it's accomplished or not. Also SOAPUI supports the use of * as a wildcard for namespaces so your expression could be:
boolean(//*:FirstName[text() = 'Francis'] and //*:LastName[text() = 'Zabala'])
And as expected value:
true
I have an XML and I want to get the Xml Node values dynamically. I want to Pass the Node Name and the in return the function or SP should return me the value of that Node.
<ClassificationTypeEntity>
<LTABLE_ID>3170</LTABLE_ID>
<LTABLE_CODE>script Code</LTABLE_CODE>
<LTABLE_DESC>alert(''hello'')</LTABLE_DESC>
<ACTIVE_YES_NO>1</ACTIVE_YES_NO>enter code here
<PRIVATE_FILING>0</PRIVATE_FILING>
<RETENTION_CODE /><RETENTION_TITLE />
<LTABLE_ID_P>0</LTABLE_ID_P>
</ClassificationTypeEntity>;
For Example, In the above xml, when I want the value of LTABLE_CODE Node, I will pass the same and the result should I get is
script code
Same for LTABLE_DESC the result should be alert(''hello'')
However I can write the 2 Xpath query for both but in case my schema gets changed (more properties added or removed) then I will have to change my SP also.
Thanks,
Mohit Jain
Thanks for your replies.
I have found the solution:
declare #xmlData xml, #NodeName VarChar(500), #nodeValue VarChar(500)
Set #NodeName = 'LTABLE_CODE';
--Set #NodeName = 'LTABLE_DESC';
set #xmlData =
'<ClassificationTypeEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LTABLE_ID>3170</LTABLE_ID>
<LTABLE_CODE><script>al</LTABLE_CODE>
<LTABLE_DESC><script>alert(''hello'')</script></LTABLE_DESC>
<ACTIVE_YES_NO>1</ACTIVE_YES_NO>
<PRIVATE_FILING>0</PRIVATE_FILING>
<RETENTION_CODE /><RETENTION_TITLE />
<LTABLE_ID_P>0</LTABLE_ID_P>
</ClassificationTypeEntity>'
SET #nodeValue = #xmlData.value('(/ClassificationTypeEntity/*[local-name()=sql:variable("#NodeName")])[1]','nvarchar(max)')
SELECT #nodeValue