Given an xpath to an attribute and a new value, I am looking to update the attribute value to the new value.
I have followed the example here: http://vtd-xml.sourceforge.net/codeSample/cs7.html and come up with the following:
autoPilot.selectXPath(xpath);
modifier.updateToken(vtdNav.getAttrVal(vtdNav.toString(autoPilot.evalXPath())), newContent);
...my tests all pass but perhaps because I am not used to the "tokenized" way that vtd-xml works, it doesn't "feel" right so I am just looking for affirmation that I've done the correct thing.
Your code will work just fine... assume you will call modifier.output().
but it is not optimal...
This statement
modifier.updateToken(vtdNav.getAttrVal(vtdNav.toString(autoPilot.evalXPath())), newContent);
Can be written as
modifier.updateToken(autoPilot.evalXPath()+1, newContent);
Because if the attribute name has an index value of i (!=-1), then the attrinute value is always i+1... as attr val immediately follows an attr name. No conditional check is needed.
Related
I'm trying to find an element that is generated from the PCA Predict API, found in this link here. http://www.pcapredict.com/en-gb/address-capture-software/
The code I have at the moment is as follows but it throws an timeout exception due to it not finding any elements. Yet the xpath is correct as I have checked it in developer tools.
By PCA = By.id("inputPCAnywhere");
driver.findElement(PCA).clear();
driver.findElement(PCA).sendKeys(ValidPostcode);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#class='pcaitem pcafirstitem']")));
driver.findElement(By.xpath("//div[#class='pcaitem pcafirstitem']")).click();
The element is visible on the page, and developer tools only returns one result that that xpath, there is no ID's to find it by.
It looks like that the first item is getting "selected" by default leading to it's class value being equal to the following:
<div class="pcaitem pcafirstitem pcaselected"...>...</div>
All other following results have only pcaitem class, but none have a pcaitem pcafirstitem class value.
In other words, your problem is the strict class match. I would improve the locator to have a partial match on the class attribute. For instance, with a CSS selector:
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".pcaitem.pcafirstitem")));
I'm trying to automate my Test Cases using Selenium for an OBIEE application. Now, I need to read a value from a tabular report generated. The problem is, the ID of the last cell where the total is, keeps on changing.
For example- Currently the id is: db_saw_9270_6_1610_0.
After refreshing, the ID becomes something else. The 4 numbers in between (9270) changes. The remaining bit are the same. I'm using the following logic to capture this element:
driver.findElement(By.xpath(".//*[contains(#id, '_6_1610_0')]")).getText();
But, it is returning org.openqa.selenium.NoSuchElementException: Unable to locate element:
Please tell me where did I go wrong and what should I do?
you can try starts-with and substring (as a substitute for xpath 2.0 methdod ends-with):
string xpath = "//*[starts-with(#id, 'db_saw_') and substring(#id, string-length(#id) - 8) = '_6_1610_0']"
driver.findElement(By.xpath(xpath)).getText();
You can try below xpath:-
driver.findElement(By.xpath("//*[starts-with(#id, 'db_saw')]")).getText();
driver.findElement(By.CSSselector("a[id*='_6_1610_0']")).getText();
Note: the a represents a html element. If your id is in a element then you have to replace a by table.
Check this out for more examples with css selector
I am trying to retrieve the value of an attribute from an xmel file using XPath and I am not sure where I am going wrong..
This is the XML File
<soapenv:Envelope>
<soapenv:Header>
<common:TestInfo testID="PI1" />
</soapenv:Header>
</soapenv:Envelope>
And this is the code I am using to get the value. Both of these return nothing..
XPathBuilder getTestID = new XPathBuilder("local-name(/*[local-name(.)='Envelope']/*[local-name(.)='Header']/*[local-name(.)='TestInfo'])");
XPathBuilder getTestID2 = new XPathBuilder("Envelope/Header/TestInfo/#testID");
Object doc2 = getTestID.evaluate(context, sourceXML);
Object doc3 = getTestID2.evaluate(context, sourceXML);
How can I retrieve the value of testID?
However you're iterating within the java, your context node is probably not what you think, so remove the "." specifier in your local-name(.) like so:
/*[local-name()='Header']/*[local-name()='TestInfo']/#testID worked fine for me with your XML, although as akaIDIOT says, there isn't an <Envelope> tag to be seen.
The XML file you provided does not contain an <Envelope> element, so an expression that requires it will never match.
Post-edit edit
As can be seen from your XML snippet, the document uses a specific namespace for the elements you're trying to match. An XPath engine is namespace-aware, meaning you'll have to ask it exactly what you need. And, keep in mind that a namespace is defined by its uri, not by its abbreviation (so, /namespace:element doesn't do much unless you let the XPath engine know what the namespace namespace refers to).
Your first XPath has an extra local-name() wrapped around the whole thing:
local-name(/*[local-name(.)='Envelope']/*[local-name(.)='Header']
/*[local-name(.)='TestInfo'])
The result of this XPath will either be the string value "TestInfo" if the TestInfo node is found, or a blank string if it is not.
If your XML is structured like you say it is, then this should work:
/*[local-name()='Envelope']/*[local-name()='Header']/*[local-name()='TestInfo']/#testID
But preferably, you should be working with namespaces properly instead of (ab)using local-name(). I have a post here that shows how to do this in Java.
If you don't care for the namespaces and use an XPath 2.0 compatible engine, use * for it.
//*:Header/*:TestInfo/#testID
will return the desired input.
It will probably be more elegant to register the needed namespaces (not covered here, depends on your XPath engine) and query using these:
//soapenv:Header/common:TestInfo/#testID
I have got two problems with using xml.
First Problem:
This code working correct. If argument have only one child but not list of childs.
doc.getDocumentElement().getChildNodes().item(t).getLastChild().getNodeValue()
I would like to prepare something like this:
If(doc.getDocumentElement().getChildNodes().item(t).getLastChild().getNodeValue() !=null)
but this doesn't work because value is never empty but I need similar If which can check is list of value or not.
Normal Node:
<name>Adam</name>
List Node:
<car>
<tire>yes</tire>
<trunk>yes</trunk>
</car>
Second Problem:
If I make nested ChildNodes I`m not able to take value do you know where is a problem?
Example of node:
<name>Adam</name>
If I wrire this I get "name"
doc.getDocumentElement().getChildNodes().item(t).getChildNodes().item(m).getNodeName()
If I will write this I will get null but I need "Adam"
doc.getDocumentElement().getChildNodes().item(t).getChildNodes().item(m).getNodeName()
answer - I should use ToLenght() > 1
This is my xml document. I want to sign only the userID part using xml signature. I am using xpath transformation to select that particular element.
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
Version="2.0" IssueInstant="2012-05-22T13:40:52:390" ProtocolBinding="urn:oasis:na
mes:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="localhos
t:8080/consumer.jsp">
<UserID>
xyz
</UserID>
<testing>
text
</testing>
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
http://localhost:8080/saml/SProvider.jsp
</saml:Issuer>
</samlp:AuthnRequest>
I am using the following code to add the transformations :
transformList.add(exc14nTransform);
transformList.add(fac.newTransform(Transform.XPATH, new XPathFilterParameterSpec("samlp:AuthnRequest/UserID xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"")));
But I get the following :
Original Exception was javax.xml.transform.TransformerException: Extra illegal t
okens: 'xmlns', ':', 'samlp', '=', '"urn:oasis:names:tc:SAML:2.0:protocol"'
So, I tried removing the xmlns part.
transformList.add(fac.newTransform(Transform.XPATH, new XPathFilterParameterSpec("samlp:AuthnRequest/UserID")));
But it signs the whole document and gives the following message :
com.sun.org.apache.xml.internal.security.utils.CachedXPa
thFuncHereAPI fixupFunctionTable
INFO: Registering Here function
What is the problem?
EDIT
As #Jörn Horstmann said the message is just a log or something like that. Now the problem is that even after giving the xpath query the whole document is signed instead of just the UserID. I confirmed this by changing the value of <testing>element after signing the document. The result is that the document does not get validated(If it signed only the UserID part, then any changes made to <testing> should result in a valid signature .)
This is not a valid xpath expression, there is no way to declare namespace prefixe inside the expression.
samlp:AuthnRequest/UserID xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
XPathFilterParameterSpec does have another constructor that allows to specify a mapping of namespace prefixes, you could try the following expression:
new XPathFilterParameterSpec("samlp:AuthnRequest/UserID",
Collections.singletonMap("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"))
Edit:
The message does not seem to be an error, see line 426 here, its log level should probably be lower than INFO though.
I also had a look at the description of xpath filtering:
The XPath expression appearing in the XPath parameter is evaluated once for each node in the input node-set. The result is converted to a boolean. If the boolean is true, then the node is included in the output node-set. If the boolean is false, then the node is omitted from the output node-set.
So the correct xpath expression to only include the UserID in the signature would be self::UserID. But don't ask me if this actually makes sense for a xml signature. The example in the specification seems to use a xpath expression to include everything except the signature element itself:
not(ancestor-or-self::dsig:Signature)
Edit 2:
The correct expression is actually ancestor-or-self::UserID since the filter also has to include the text child nodes of the UserID node.