Given XML like this:
...
<Sport SportId="1">
<Name language="en">Soccer</Name>
<Name language="fi">Jalkapallo</Name>
...
</Sport>
...
How can I, using the Simple XML Framework, read the two values into fields in a Java class? (The <Sport> element is already correctly mapped to the corresponding class.)
public class Sport {
...
String nameEn;
String nameFi;
...
}
I've tried approaches like:
#Element(name = "Name")
#Path("Name[#language='en']")
String nameEn;
But the parsing fails with:
Exception in thread "main" org.simpleframework.xml.core.PathException:
Invalid index for path '[#language='en']' in field 'nameEn'
Also, omitting #Element like this:
#Path("Name[#language='en']")
String nameEn;
...parsing doesn't crash, but nameEn value stays null.
I'd like the matching to be based on the language attribute (instead of ordering), but I'm wondering if that's possible (maybe XPath support in Simple Framework is limited?).
Have you tried getting the text of the element explicitly? i.e. Name[#language='en']/text()
Your Xpath is selecting the element, not the text of the element, which can cause some XML engines to choke.
Related
So I have the following input, expected output and actual output xml:
input.xml
<Request>
<EmailSubjectLine>Main Contact & No Reported To</EmailSubjectLine>
<ProductRq>
<Signon>
<ClientDt>1/6/2017 11:25:45 AM</ClientDt>
<CustLangPref>en-US</CustLangPref>
</Signon>
<SvcRq>
<RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID>
<NotificationRq>
<TransactionRequestDt>2017-01-06</TransactionRequestDt>
<Currency>USD</Currency>
</NotificationRq>
</SvcRq>
</ProductRq>
<!-- rest of input -->
</Request>
expected-output.xml
<ProductRq xmlns="http://test.org/standards/intake">
<Audit>
<TransID>Test</TransID>
</Audit>
<Signon>
<ClientDt>1/6/2017 11:25:45 AM</ClientDt>
<CustLangPref>en-US</CustLangPref>
</Signon>
<SvcRq>
<RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID>
<NotificationRq>
<RqUID>Test</RqUID>
<TransactionRequestDt>2017-01-06</TransactionRequestDt>
<Currency>USD</Currency>
</NotificationRq>
</SvcRq>
<!-- rest of expected-output -->
</ProductRq>
actual-output.xml
<ProductRq xmlns="http://test.org/standards/intake">
<Audit>
<TransID>123534Abwe-asdcv-1258qw-asd</TransID>
</Audit>
<Signon>
<ClientDt>1/6/2017 11:25:45 AM</ClientDt>
<CustLangPref>en-US</CustLangPref>
</Signon>
<SvcRq>
<RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID>
<NotificationRq>
<RqUID>CG-17Dawe-12354-Hw35Sf</RqUID>
<TransactionRequestDt>2017-01-06</TransactionRequestDt>
<Currency>USD</Currency>
</NotificationRq>
</SvcRq>
<!-- rest of actual-output -->
</ProductRq>
I'm comparing them with the following Diff set up:
MyTest.java
Diff diff = DiffBuilder
.compare(xmlExpectedOutput)
.withTest(xmlOutput)
.normalizeWhitespace()
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder()
.whenElementIsNamed("Audit")
.thenUse(ElementSelectors.byXPath("./TransID", ElementSelectors.byName))
.whenElementIsNamed("NotificationRq")
.thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName))
.elseUse(ElementSelectors.byNameAndText)
.build()
))
.checkForSimilar()
.build();
I get the following differences when I run the above input and compare with expected-output.xml:
[Expected child '{http://test.org/standards/intake}RqUID' but was 'null' - comparing <RqUID...> at /ProductRq[1]/SvcRq[1]/NotificationRq[1]/RqUID[1] to <NULL> (DIFFERENT), Expected child 'null' but was '{http://test.org/standards/intake}RqUID' - comparing <NULL> to <RqUID...> at /ProductRq[1]/SvcRq[1]/NotificationRq[1]/RqUID[1] (DIFFERENT)]
I don't get why my Element selector wouldn't work, am I using it incorrectly? My aim is whenever TransmissionId or NotificationRq/RqUID are found, to match them with the expected output versions by name only, otherwise use name and text for other elements as these elements contain unique generated ids that change every test run and can't be predicted(with a view to creating a more complex selector later, e.g. to compare ProductRq via name and attribute as a namespace is added to this). Is there something I'm missing, and am I able to combine the 2 XPath selectors together rather than several when/then lines and the default case?
Note: the xml is transformed via xslt. The namespace on PRoductRq is not there on the source document; the source is copied, the namespace added to ProductRq and then sent for output along with some element removals/modifications/additions
XMLUnit says the RqUID elements inside the NotificationRq wouldn't match and of course they are different.
.whenElementIsNamed("NotificationRq")
.thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName))
means: when XMLUnit tries to find a partner for an NotificationRq element then it has to search for an NotificationRq that has an RqUID child - and only use the RqUID element.
It doesn't set up any rules for any other element, in particular RqUID itself. For RqUID elements the default rules apply and
.elseUse(ElementSelectors.byNameAndText)
says: XMLUnit only accepts two elements as pairs if their names and the nested text match. Which is not the case for the RqUID elements in question.
Your whole ElementSelector says
match Audits if they have TransID children of arbitrary content.
match NotificationRqs if they have RqUID of arbitrary content.
use element name and nested text otherwise
which doesn't fit your example. Looking at your XML you probably wanted
match almost everything by element name and nested text (although from the example the element name would be enough)
ignore the nested text of TransId children of Audits
ignore the nested text of RqUID children of NotificationRq
There is no built-in predicate for "element named foo if it is a child of an element named bar", it could be something like
Predicate<Element> transIdInAudit = e -> {
if (e == null || e.getParentNode() == null) {
return false;
}
return "TransID".equals(e.getLocalName()) && "Audit".equals(e.getParentNode().getLocalName());
};
which you likely want to make generalizable :-)
With that you'd use
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder()
.when(transIdInAudit)
.thenUse(ElementSelectors.byName)
.when(rqUIDInNotificationRq) // similar to transIdInAudit
.thenUse(ElementSelectors.byName)
.elseUse(ElementSelectors.byNameAndText)
.build())
Maybe you really want to match SvcRq if they have matching RqUID, maybe not. If so you'd use the structure you currently use for NotificationRq.
This in itself will not be enough to ignore the nested text of the matched TransId and RqUID elements, it will only ensure XMLUnit will pick the nodes you want it to use. For the nested text you'll need a DifferenceEvaluator.
Given that you are using ElementSelectors.byNameAndText by default, you know the nested texts are the same for all matched nodes except for the two specific elements where you want to ignore the content. So a DifferenceEvaluator like
DifferenceEvaluators.chain(DifferenceEvaluators.Default,
DifferenceEvaluators.downgradeDifferencesToEqual(ComparisonType.TEXT_VALUE))
should work.
I'm currently working on a txt-to-xml project. Basically what I'm doing is creating different XmlElements for some of the content.
I got a DTD up and running and for now I'm creating a default xml, just to make sure every xml created is a valid xml (for the DTD given).
I'm mainly creating new Classes for every Element, which doesn't have a #PCDATA structure and it's working pretty fine so far.
Now I'm struggling with a problem:
I got the following in my DTD:
<!ELEMENT REACTION(#PCDATA | ACTOR*)>
What I'm looking for in my Text is something like:
Prof. X clapped!
and I want to extract this into my XML as:
<REACTION>
<ACTOR>Prof. X</ACTOR> clapped!
</REACTION>
So what I basically want is a String-Attribute within the ReactionClass which is devlares as XML-Element but holds an Actor-Attribute + Rest of the Text. I thought of something like:
String m_sText;
String m_sActor;
public ReactionClass(){
this.Actor = "Prof. X";
this.sText = this.m_sActor + " clapped!";
}
#XmlElement(name = "TEXT")
public String getM_sText(){ return this.m_sText; }
#XmlElement(name = "ACTOR")
public String getM_sActor(){ return this.m_sActor; }
For all other Nodes, such as the RootNode I created a RootNodeClass which holds different attributes, such as m_nLocation, m_nTime, m_nYear which are declared as XML-Elements, so the JAXB-Marshaller just builds up the XML on basis of these elements:
<ROOT>
<TIME>09:00</TIME>
<LOCATION>New York</TIME>
<YEAR>1992</YEAR>
</ROOT>
I wanted to do the same with the REACTION-Node (like mentioned above), but when creating a new Class REACTION I'm getting sth. like:
<REACTION>
<TEXT>Prof. X clapped!</TEXT>
<ACTOR>Prof. X</ACTOR>
</REACTION>
How would I put them into one Element but still keep the Tags such as above?
If anybody got an idea how to manage this I would be very thankful!
Thanks Max
First, what you most probably need is #XmlMixed. You'll probably have a structure like:
#XmlMixed
#XmlElementRefs({
#XmlElementRef(name="ACTOR", type=JAXBElement.class),
...})
List<Object> content;
With this you could put there Strings and JAXBElement<Actor> to achieve so-called mixed content.
Next, you might consider turning your DTD into XML Schema first and compiling it - or compiling the DTD with XJC.
Finally, what you have is so-called "semi-structured data" which I think is not quite suitable for JAXB. JAXB works great for strong and clear structures, but if you have mixed stuff you get weird models that are hard to work with. I can't suggest an alternative though.
I do REST calls to a WebService and receive always XML as response. Then i'm parsing that XML und filling Java objects with those informations.
The Problem is that the element-tags could have different namespaces, like this:
<ns:title>....</ns:title>
or
<ns2:title>....<ns2:title>
or
<title>...<title>
EDIT:
And the namespace URIs look like this:
<ns2:feed xmlns="http://www.example.com/routing1/routing2"
xmlns:ns2="http://www.w3.org/../Atom"
xmlns:ns3="http://www.example.com/routing1/routing2"
xmlns:ns4="http://purl.org/routing1/routing2/1.0">
So therefore i changed the method element.getElementsByTagNameNS("specifiedNamespace", "title") to element.getElementsByTagNameNS("*", "title").
Is that okay to match all namespace, because i have also the case that the element-tag doesn't have a namespace like the third example <title>..</title>..
Is there a better procedure, to solve that problem? Or is it okay to solve it like, how i do it?
Thanks.
EDIT: 2 response examples
1.
<ns2:feed xmlns="http://www.example.com/routing1/routing2" xmlns:ns2="http://www.w3.org/../Atom" xmlns:ns3="http://www.example.com/routing1/routing2" xmlns:ns4="http://purl.org/routing1/routing2/1.0">
...
<ns2:someTag1>..</ns2:someTag1>
<ns2:title>title</ns2:title>
<entry>...</entry>
....
</ns2:feed>
2
<ns2:feed xmlns="http://www.w3.org/../Atom" xmlns:ns2="http://www.example.com/routing1/routing2" xmlns:ns3="http://www.example.com/routing1/routing2" xmlns:ns4="http://purl.org/routing1/routing2/1.0">
...
<someTag1>..<someTag1>
<title>title<title>
<ns2:entry>...</ns2:entry>
....
</ns2:feed>
Your title elements have the same namespace in both of your examples.
In the first example, you have:
xmlns:ns2="http://www.w3.org/../Atom"
and
<ns2:title>title</ns2:title>
so this means that title is in the http://www.w3.org/../Atom namespace.
In the second example, you have:
xmlns="http://www.w3.org/../Atom"
and
<title>title<title>
so here again title is in the http://www.w3.org/../Atom namespace.
The prefixes are different (the second example isn't using a prefix for title), but the namespace is the same.
This means that you should be able to use:
element.getElementsByTagNameNS("http://www.w3.org/../Atom", "title")
and it should successfully select the title element, even if the prefixes change.
How to convert String to By type.
Following is my scenario:
Keep object identification in Properties file in below manner
username=By.id("username")
password=By.id("password")
In the application i would like to retrieve the values like
Properties prop=new Properties();
prop.load("above properties file path")
driver.findelement(prop.getProperty("username")) //Here in eclipse it is complaining saying "The method findElement(By) in the type WebDriver is not applicable for the arguments (String)"
So can somebody help me in this?
I can use like below or some other format, but i want solution for the above
username="//*[#id='username']"
username="username"
driver.findElement(By.xpath(prop.getProperty("username"))
driver.findElement(By.id(prop.getProperty("username"))
You can create one parser method which will return desired locator object something like below:
public static By locatorParser(String locator) {
By loc = By.id(locator);
if (locator.contains("id"))
loc = By.id(locator.substring(locator.indexOf("\"") + 1,
locator.length() - 2));
else if (locator.contains("name"))
loc = By.name(locator.substring(locator.indexOf("\"") + 1,
locator.length() - 2));
if (locator.contains("xpath"))
loc = By.xpath(locator.substring(locator.indexOf("\"") + 1,
locator.length() - 2));
return loc;
}
Can be called in your code in the following way:
driver.findElement(locatorParser(prop.getProperty("username")));
Added logic for id, name, xpath. You can modify the same method to add all the available locators. Hope this helps!
The WebDriver.findElement method accepts only an object parameter of the type By.
The Property.getProperty method returns only a String typed object.
Therefore, this may be what fits your need:
WebElement element = driver.findElement(By.name(prop.getProperty("username")));
You can't force a String typed object into a method that accepts only a By typed object. When you ask Selenium to find a String "username" you have to tell it more than just the string's value.
The method By.[method] you choose all depends on what you are looking for in the page that Selenium is searching. "username" is most likely the "name" (By.name) or "id" (By.Id) of the field you are looking for. The By class refines the search to where you expect the String "username" to be: in a name, id, tag, class, etc. See the By class definition.
Also, take caution as the getProperty method could return a null, and the By methods with throw an IllegalArgumentException if you pass it a null string. So providing a default return value ("") for getProperty is usually safer.
WebElement element = driver.findElement(By.name(prop.getProperty("username", "")));
You have to evaluate the entire expression within the context of existing code. You should framework such as JEXL or expressionoasis
Code below uses JEXL
// Create JexlEngine instance
JexlEngine jexl = new JexlEngine();
// Create Expression object for the string
Expression e = jexl.createExpression(prop.getProperty("username"));
// Create a context. You can pass reference to any object you want to use inside the expression
JexlContext jc = new MapContext();
// Now evaluate the expression, getting the result
driver.findElement((By)e.evaluate(jc));
I think you are trying to implement page object model using properties file. What I would suggest here to use xml file instead of java properties file. for example sample xml for page elements would look like below.
<Pages>
<LoginPage>
<txtUserName by="id">username</txtUserName>
<txtPassword by="id">password</txtPassword>
</LoginPage>
</Pages>
Now you can write methods retrieve nodes from the xml file and node attribute as id,xpath etc... further write methods to find elements using xml node value and attribute. I have used same method in my project as it works great for me.
I'm trying to use this in JSF to dynamically show the title of a page.
<h:panelGroup rendered="#{not empty searchBean.fsResultsTitleOne}"><h2>#{msgs.fireStudySearchTitle}</h2></h:panelGroup>
And I'm getting this error:
rendered="#{not empty searchBean.fsResultsTitleOne}": Property 'fsResultsTitleOne' not found on type
However, I did define it in on the type like this:
private String fsResultsTitleOne;
public String getFSResultsTitleOne(){return fsResultsTitleOne;}
public void setFSResultsTitleOne(String newValue){fsResultsTitleOne = newValue;}
And set it to something:
setFSResultsTitleOne("I'm not Empty!");
And even used this to make sure it would be set:
System.out.println("This is the FS Results Page Title: " + fsResultsTitleOne);
And it seems to be working:
This is the FS Results Page Title: I'm not Empty!
Am I setting it wrong someplace?
Change
getFSResultsTitleOne
setFSResultsTitleOne
to
getFsResultsTitleOne
setFsResultsTitleOne
The way how JSF accesses the properties in your code is it adds a "get" to the property name with the first letter of the property in caps.
E.g. If you write in the xhtml page as -
value="#{myBean.name}"
Good coding style says you must have private property with respective getters and setters. So JSF parser in order to access the property will convert the request as following-
value = myBean.getName()
Note that the N is in caps.
So if you mess with the name of the property as you did, the parser will be happy enough to throw a PropertNotFoundException.