Java - attributevalue from parents child - java

I'm currently working on a small weather API (From YR.NO) in Java.
The API is in XML as shown below:
NOTE : There are several of those "boxes" in the same API, just different time on them.
Whole XML shown
<time datatype="forecast" from="2016-09-08T21:00:00Z" to="2016-09-08T21:00:00Z">
<location altitude="47" latitude="59.3293235" longitude="18.0685808">
<temperature id="TTT" unit="celsius" value="12.0"/>
<windDirection id="dd" deg="121.8" name="SE"/>
<windSpeed id="ff" mps="2.2" beaufort="2" name="Svak vind"/>
<windGust id="ff_gust" mps="3.7"/>
<humidity value="80.5" unit="percent"/>
<pressure id="pr" unit="hPa" value="1016.0"/>
<cloudiness id="NN" percent="51.6"/>
<fog id="FOG" percent="-0.0"/>
<lowClouds id="LOW" percent="51.2"/>
<mediumClouds id="MEDIUM" percent="0.0"/>
<highClouds id="HIGH" percent="0.8"/>
<dewpointTemperature id="TD" unit="celsius" value="8.8"/>
</location>
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException
{
for (int i = 0; i < attributes.getLength(); i++)
{
String attributeName = attributes.getLocalName(i);
String attributeValue = attributes.getValue(i);
System.out.println(attributes.getLocalName(i) + " : " + attributes.getValue(i));
//if(attributeValue.toLowerCase().indexOf(timeCheck.toLowerCase()) != -1)
// {
// System.out.println("Temperature: " + attributes.getValue(i));
// }
}
With this ^ i can easily display all the Names with their values but I can't really figure out how to control it.
What I have now is a String that saves the user-input like:
String timeCheck = "T"+timeUserInput;
(If i input 15 then the timeCheck becomes "T15")
and then I check if some value obtains this input for example "T21" with this inside the for-loop:
if(attributeValue.toLowerCase().indexOf(timeCheck.toLowerCase()) != -1)
and then if's true I want to print the value of temperature which in this case is "12.0" but I can't seem to find an easy way of doing it. I can print ALL the temperature values but i ONLY want the temperature value of the right time.
Tried my best explaining my issue, recently started with Java, Thanks in advance! If you need an explanation of anything just tell me and i'll try.

To your purposes, I'd recommend you to use XPATH Api, a specific language to query an XML. The program would be like this:
public void getTemperature(org.w3c.dom.Document doc)
throws javax.xml.transform.TransformerException, javax.xml.xpath.XPathExpressionException
{
javax.xml.xpath.XPathFactory xpathFactory=javax.xml.xpath.XPathFactory.newInstance();
javax.xml.xpath.XPath xpath=xpathFactory.newXPath();
String temperature=(String)xpath.evaluate("product/time[contains(#from,'"+timeCheck+"')]/location/temperature/#value", doc, javax.xml.xpath.XPathConstants.STRING);
}

Related

Make link inside of string clickable: Java

I am creating a messaging system in Java using android studio.
People can send messages back and forth. But if they send a link, it just shows up as regular text. I want the part that is the link to show up as a clickable link and the rest just text.
I checked all day on this site and others but no seems to do this in the way I'm trying too. Most of the answers I see are people using a TexView to accomplish their goal. I'm using a string. Can someone please help me figure this out ?
private void showMessages(){
DatabaseReference userMessageKeyRef = RootRef.child("Messages").child(messageSenderID).child(messageReceiverID);
userMessageKeyRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot snapshot1 : snapshot.getChildren()) {
Messages messages = new Messages();
String strMessage = snapshot1.child("message").getValue().toString();
String strFrom = snapshot1.child("from").getValue().toString();
String strType = snapshot1.child("type").getValue().toString();
messages.setMessage(strMessage);
messages.setFrom(strFrom);
messages.setType(strType);
messagesList.add(messages);
// Pattern for recognizing a URL, based off RFC 3986
final Pattern urlPattern = Pattern.compile(
"(?:^|[\\W])((ht|f)tp(s?):\\/\\/|www\\.)"
+ "(([\\w\\-]+\\.){1,}?([\\w\\-.~]+\\/?)*"
+ "[\\p{Alnum}.,%_=?&#\\-+()\\[\\]\\*$~#!:/{};']*)",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
// separate input by spaces ( URLs don't have spaces )
String [] parts = strMessage.split("\\s+");
// get every part
for( String item : parts ) {
if(urlPattern.matcher(item).matches()) {
//it's a good url
System.out.print(""+ item + " " );
} else {
// it isn't a url
System.out.print(item + " ");
}
}
}
messageAdapter = new MessageAdapter(ChatActivity.this,messagesList);
userMessagesList.setAdapter(messageAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
There are two common ways to do this. One, like you have done, is to add html to the string. The second is to use the TextView's auto link mask feature.
Using HTML
Once you have identified URLs in your incoming string and added the appropriate html tags to turn them into links, you just need to use HtmlCompat when you go to actually display it in the TextView. You also need to make sure to call setMovementMethod or you won't be able to click the link. The advantage of using HTML is that you can have the link text be a readable phrase instead of a URL.
String txt = "This is www.google.com";
TextView link = findViewById(R.id.link);
link.setMovementMethod(LinkMovementMethod.getInstance());
link.setText(HtmlCompat.fromHtml(txt,HtmlCompat.FROM_HTML_MODE_LEGACY));
If you choose to go this route, your existing code just needs to be modified a bit to save the HTML string in the messages list passed to the adapter, then add the TextView calls above inside the adapter when you set the text.
String [] parts = strMessage.split("\\s+");
// replace URL parts with html links
for( int i = 0; i < parts.length; ++i ) {
if(urlPattern.matcher(parts[i]).matches()) {
parts[i] = "" + parts[i] + "";
}
}
// re-join parts back into a single string
String htmlMessage = String.join(" ", parts);
// save a list of html strings to pass to your adapter
htmlMessageStrings.add(htmlMessage);
Using Link Mask
This method doesn't require you to edit the string at all. If you use Linkify.ALL it also recognizes things like web links, emails, phone numbers, and physical addresses - not just web links. If you only want it to recognize web links use Linkify.WEB_URLS instead. This requires a lot less code on your part - you no longer have to try to parse the string for links.
String txt = "This is www.google.com"; // no need to modify the string
TextView link = findViewById(R.id.link);
link.setAutoLinkMask(Linkify.ALL); // or Linkify.WEB_URLS
link.setText(txt);
You can also add android:autoLink="all" to the TextView XML definition instead of calling it in-code.
Both methods produce this output

SAX Parsing in Android Searching Attribute

Im trying to identify an attribute using SAX. My XML is as follows
<DAY VALUE="1">
<BREAKFAST>0700</BREAKFAST>
<LUNCH>1200</LUNCH>
<DINNER>1900</DINNER>
</DAY>
<DAY VALUE="2">
<BREAKFAST>0730</BREAKFAST>
<LUNCH>1230</LUNCH>
<DINNER>1930</DINNER>
</DAY>
and my startElement code is as follows:
#Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
elementOn = true;
if (localName.equals("DAY"))
{
data = new XMLGettersSetters();
}
}
The above code will find the first element "DAY" and give me the contents but not the remaining DAY elements.
Is there a way I can search for the attribute? I am aiming to get the current day in the month and then search for the day within the xml. For example if today is the 2nd then I want to search in the XML file for DAY VALUE="2" and get the sub elements
==EDIT
If i modify my XML to
<DAY_1>
<BREAKFAST>0700</BREAKFAST>
<LUNCH>1200</LUNCH>
<DINNER>1900</DINNER>
</DAY_1>
<DAY_2>
<BREAKFAST>0730</BREAKFAST>
<LUNCH>1230</LUNCH>
<DINNER>1930</DINNER>
</DAY_2>
and code to:
if (localName.equals("DAY_2"))
{
data = new XMLGettersSetters();
}
I get a NullPointerException however the name if the class is not highlighted in logcat. It was referring to another class but i commented out those lines
you can get attribute value like this:
if (localName.equals("DAY")){
String day = "";
if (attributes.getValue("VALUE") != null) {
day = attributes.getValue("VALUE");
}
//data = new XMLGettersSetters();
}

Why did qName work and LocalName did not?

I was learning the Java SAX API. I made my own XML feed using php. Here is the XML Document
Now when i wanted to make my application output to the console nothing came up. I pinpointed the problem to the endElement method in my XMLHandler that extends DefaultHandler. Here is my implementation of it.
public void endElement(String uri, String localName, String qName) throws SAXException {
//I added the next three lines for debugging
System.out.println("Found End Element " + count + " times");
System.out.println("Localname = " + localName);
System.out.println("QName = " + qName);
super.endElement(uri, localName, qName);
if (this.currentItem != null){
if (localName.equalsIgnoreCase(me.osama.XMLParsing.BaseFeedParser.ITEMNAME)){
currentItem.setItemName(builder.toString());
} else if (localName.equalsIgnoreCase(me.osama.XMLParsing.BaseFeedParser.ITEMSITE)){
currentItem.setItemSite(builder.toString());
} else if (localName.equalsIgnoreCase(me.osama.XMLParsing.BaseFeedParser.ITEMNO)){
currentItem.setItemNo(builder.toString());
} else if (localName.equalsIgnoreCase(me.osama.XMLParsing.BaseFeedParser.ITEM)){
System.out.println(currentItem);
items.add(currentItem);
}
builder.setLength(0);
}
count++;
}
Turns out that localName kept on coming empty hence the conditions never held true and the code never went into the decision block. On the other hand qName brought all names out properly and once i changed the variable to qName the List<Item> items collection type did fill up and worked correctly.
I am here to ask why did qName work and not localName? Whereas the tutorial from IBM's DeveloperWorks used an RSS feed and localName worked perfectly for him.
P.S. this is the feed the IBM Tutorial used: http://www.androidster.com/android_news.rss
As per the SAX namespace for Java API,
By default, an XML reader will report a Namespace URI and a localName
for every element that belongs in a namespace, in both the start and
end handler.
Perhaps, if you add a namespace to the XML and define your elements in that namespace, it would return a valid localName. The article also mentions that with namespace processing, some implementations will return empty qName.

How to get node contents from JDOM

I'm writing an application in java using import org.jdom.*;
My XML is valid,but sometimes it contains HTML tags. For example, something like this:
<program-title>Anatomy & Physiology</program-title>
<overview>
<content>
For more info click here
<p>Learn more about the human body. Choose from a variety of Physiology (A&P) designed for complementary therapies.&#160; Online studies options are available.</p>
</content>
</overview>
<key-information>
<category>Health & Human Services</category>
So my problem is with the < p > tags inside the overview.content node.
I was hoping that this code would work :
Element overview = sds.getChild("overview");
Element content = overview.getChild("content");
System.out.println(content.getText());
but it returns blank.
How do I return all the text ( nested tags and all ) from the overview.content node ?
Thanks
content.getText() gives immediate text which is only useful fine with the leaf elements with text content.
Trick is to use org.jdom.output.XMLOutputter ( with text mode CompactFormat )
public static void main(String[] args) throws Exception {
SAXBuilder builder = new SAXBuilder();
String xmlFileName = "a.xml";
Document doc = builder.build(xmlFileName);
Element root = doc.getRootElement();
Element overview = root.getChild("overview");
Element content = overview.getChild("content");
XMLOutputter outp = new XMLOutputter();
outp.setFormat(Format.getCompactFormat());
//outp.setFormat(Format.getRawFormat());
//outp.setFormat(Format.getPrettyFormat());
//outp.getFormat().setTextMode(Format.TextMode.PRESERVE);
StringWriter sw = new StringWriter();
outp.output(content.getContent(), sw);
StringBuffer sb = sw.getBuffer();
System.out.println(sb.toString());
}
Output
For more info clickhere<p>Learn more about the human body. Choose from a variety of Physiology (A&P) designed for complementary therapies.&#160; Online studies options are available.</p>
Do explore other formatting options and modify above code to your need.
"Class to encapsulate XMLOutputter format options. Typical users can use the standard format configurations obtained by getRawFormat() (no whitespace changes), getPrettyFormat() (whitespace beautification), and getCompactFormat() (whitespace normalization). "
You could try using method getValue() for the closest approximation, but what this does is concatenate all text within the element and descendants together. This won't give you the <p> tag in any form. If that tag is in your XML like you've shown, it has become part of the XML markup. It'd need to be included as <p> or embedded in a CDATA section to be treated as text.
Alternatively, if you know all elements that either may or may not appear in your XML, you could apply an XSLT transformation that turns stuff which isn't intended as markup into plain text.
Well, maybe that's what you need:
import java.io.StringReader;
import org.custommonkey.xmlunit.XMLTestCase;
import org.custommonkey.xmlunit.XMLUnit;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
import org.testng.annotations.Test;
import org.xml.sax.InputSource;
public class HowToGetNodeContentsJDOM extends XMLTestCase
{
private static final String XML = "<root>\n" +
" <program-title>Anatomy & Physiology</program-title>\n" +
" <overview>\n" +
" <content>\n" +
" For more info click here\n" +
" <p>Learn more about the human body. Choose from a variety of Physiology (A&P) designed for complementary therapies.&#160; Online studies options are available.</p>\n" +
" </content>\n" +
" </overview>\n" +
" <key-information>\n" +
" <category>Health & Human Services</category>\n" +
" </key-information>\n" +
"</root>";
private static final String EXPECTED = "For more info click here\n" +
"<p>Learn more about the human body. Choose from a variety of Physiology (A&P) designed for complementary therapies.&#160; Online studies options are available.</p>";
#Test
public void test() throws Exception
{
XMLUnit.setIgnoreWhitespace(true);
Document document = new SAXBuilder().build(new InputSource(new StringReader(XML)));
List<Content> content = document.getRootElement().getChild("overview").getChild("content").getContent();
String out = new XMLOutputter().outputString(content);
assertXMLEqual("<root>" + EXPECTED + "</root>", "<root>" + out + "</root>");
}
}
Output:
PASSED: test on instance null(HowToGetNodeContentsJDOM)
===============================================
Default test
Tests run: 1, Failures: 0, Skips: 0
===============================================
I am using JDom with generics: http://www.junlu.com/list/25/883674.html
Edit: Actually that's not that much different from Prashant Bhate's answer. Maybe you need to tell us what you are missing...
If you're also generating the XML file you should be able to encapsulate your html data in <![CDATA[]]> so that it isn't parsed by the XML parser.
The problem is that the <content> node doesn't have a text child; it has a <p> child that happens to contain text.
Try this:
Element overview = sds.getChild("overview");
Element content = overview.getChild("content");
Element p = content.getChild("p");
System.out.println(p.getText());
If you want all the immediate child nodes, call p.getChildren(). If you want to get ALL the child nodes, you'll have to call it recursively.
Not particularly pretty but works fine (using JDOM API):
public static String getRawText(Element element) {
if (element.getContent().size() == 0) {
return "";
}
StringBuffer text = new StringBuffer();
for (int i = 0; i < element.getContent().size(); i++) {
final Object obj = element.getContent().get(i);
if (obj instanceof Text) {
text.append( ((Text) obj).getText() );
} else if (obj instanceof Element) {
Element e = (Element) obj;
text.append( "<" ).append( e.getName() );
// dump all attributes
for (Attribute attribute : (List<Attribute>)e.getAttributes()) {
text.append(" ").append(attribute.getName()).append("=\"").append(attribute.getValue()).append("\"");
}
text.append(">");
text.append( getRawText( e )).append("</").append(e.getName()).append(">");
}
}
return text.toString();
}
Prashant Bhate's solution is nicer though!
If you want to output the content of some JSOM node just use
System.out.println(new XMLOutputter().outputString(node))

How to insert/replace XML tag in XmlDocument?

I have a XmlDocument in java, created with the Weblogic XmlDocument parser.
I want to replace the content of a tag in this XMLDocument with my own data, or insert the tag if it isn't there.
<customdata>
<tag1 />
<tag2>mfkdslmlfkm</tag2>
<location />
<tag3 />
</customdata>
For example I want to insert a URL in the location tag:
<location>http://something</location>
but otherwise leave the XML as is.
Currently I use a XMLCursor:
XmlObject xmlobj = XmlObject.Factory.parse(a.getCustomData(), options);
XmlCursor xmlcur = xmlobj.newCursor();
while (xmlcur.hasNextToken()) {
boolean found = false;
if (xmlcur.isStart() && "schema-location".equals(xmlcur.getName().toString())) {
xmlcur.setTextValue("http://replaced");
System.out.println("replaced");
found = true;
} else if (xmlcur.isStart() && "customdata".equals(xmlcur.getName().toString())) {
xmlcur.push();
} else if (xmlcur.isEnddoc()) {
if (!found) {
xmlcur.pop();
xmlcur.toEndToken();
xmlcur.insertElementWithText("schema-location", "http://inserted");
System.out.println("inserted");
}
}
xmlcur.toNextToken();
}
I tried to find a "quick" xquery way to do this since the XmlDocument has an execQuery method, but didn't find it very easy.
Do anyone have a better way than this? It seems a bit elaborate.
How about an XPath based approach? I like this approach as the logic is super-easy to understand. The code is pretty much self-documenting.
If your xml document is available to you as an org.w3c.dom.Document object (as most parsers return), then you could do something like the following:
// get the list of customdata nodes
NodeList customDataNodeSet = findNodes(document, "//customdata" );
for (int i=0 ; i < customDataNodeSet.getLength() ; i++) {
Node customDataNode = customDataNodeSet.item( i );
// get the location nodes (if any) within this one customdata node
NodeList locationNodeSet = findNodes(customDataNode, "location" );
if (locationNodeSet.getLength() > 0) {
// replace
locationNodeSet.item( 0 ).setTextContent( "http://stackoverflow.com/" );
}
else {
// insert
Element newLocationNode = document.createElement( "location" );
newLocationNode.setTextContent("http://stackoverflow.com/" );
customDataNode.appendChild( newLocationNode );
}
}
And here's the helper method findNodes that does the XPath search.
private NodeList findNodes( Object obj, String xPathString )
throws XPathExpressionException {
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expression = xPath.compile( xPathString );
return (NodeList) expression.evaluate( obj, XPathConstants.NODESET );
}
How about an object oriented approach? You could deserialise the XML to an object, set the location value on the object, then serialise back to XML.
XStream makes this really easy.
For example, you would define the main object, which in your case is CustomData (I'm using public fields to keep the example simple):
public class CustomData {
public String tag1;
public String tag2;
public String location;
public String tag3;
}
Then you initialize XStream:
XStream xstream = new XStream();
// if you need to output the main tag in lowercase, use the following line
xstream.alias("customdata", CustomData.class);
Now you can construct an object from XML, set the location field on the object and regenerate the XML:
CustomData d = (CustomData)xstream.fromXML(xml);
d.location = "http://stackoverflow.com";
xml = xstream.toXML(d);
How does that sound?
If you don't know the schema the XStream solution probably isn't the way to go. At least XStream is on your radar now, might come in handy in the future!
You should be able to do this with query
try
fn:replace(string,pattern,replace)
I am new to xquery myself and I have found it to be a painful query language to work with, but it does work quiet well once you get over the initial learning curve.
I do still wish there was an easier way which was as efficient?

Categories