Parsing Facebooks XML query response problem - java

Hey guys! :)
I'm working on a service wich catches the number of likes, shares and so on from the Facebook API.
But there is a problem because i get a nullpointer exception from fqlResponse.getChild().
In my opinion there is a problem with the automatic detection of the xmls doctype, so i defined the doctype manually, but the problem still exists. Maybe the facebooks xml doctype isn't correct?
Here is the catching method:
public void refreshLikesSharesClicksAndTotal() throws JDOMException, IOException {
URL fqlURL = new URL("https://api.facebook.com/method/fql.query?query=select like_count, total_count, share_count, click_count from link_stat where url=\"" + this.url.toString() + "\"");
Document inputXML = new SAXBuilder().build(fqlURL);
DocType docType = new DocType("xml", "http://www.w3.org/2001/XMLSchema-instance");
inputXML.setDocType(docType);
Element fqlResponse = inputXML.getRootElement().getChild("link_stat");
Element likes = fqlResponse.getChild("like_count");
logger.info("Likes: " + likes.getText());
Element shares = fqlResponse.getChild("share_count");
Element clicks = fqlResponse.getChild("click_count");
Element total = fqlResponse.getChild("total_count");
this.likes = Integer.parseInt(likes.getText());
this.shares = Integer.parseInt(shares.getText());
this.clicks = Integer.parseInt(clicks.getText());
this.total = Integer.parseInt(total.getText());
}
XML Example:
https://api.facebook.com/method/fql.query?query=select%20%20like_count,%20total_count,%20share_count,%20click_count%20from%20link_stat%20where%20url=%22http://heise.de%22
German problem description:
http://www.java-forum.org/xml-co/118648-problem-beim-parsen-facebook-xml.html
Thanks for help!
whitenexx

You can do something like this:-
URL url = new URL("https://api.facebook.com/method/fql.query?query=select%20like_count,%20total_count,%20share_count,%20click_count%20from%20link_stat%20where%20url=%22http://heise.de%22");
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(url.openStream());
XPath xPath = XPathFactory.newInstance().newXPath();
int likeCount = Integer.parseInt(xPath.evaluate("//like_count/text()", doc));
int totalCount = Integer.parseInt(xPath.evaluate("//total_count/text()", doc));
int shareCount = Integer.parseInt(xPath.evaluate("//share_count/text()", doc));
int clickCount = Integer.parseInt(xPath.evaluate("//click_count/text()", doc));
Make sure you encode the spaces and double quotes in the URL with %20 and %22 appropriately. I have tested this code, and it works for me.

It works for me:
public void refreshLikesSharesClicksAndTotal() throws JDOMException, IOException {
URL fqlURL = new URL("https://api.facebook.com/method/fql.query?query=select%20like_count,%20total_count,%20share_count,%20click_count%20from%20link_stat%20where%20url=%22http://heise.de%22");
URLConnection openConnection = fqlURL.openConnection();
String contentType = openConnection.getContentType();
Document inputXML = new SAXBuilder().build(fqlURL);
DocType docType = new DocType("xml", "http://www.w3.org/2001/XMLSchema-instance");
inputXML.setDocType(docType);
Element root = inputXML.getRootElement();
Element fqlResponse = root.getChild("link_stat", root.getNamespace());
Element likes = fqlResponse.getChild("like_count", root.getNamespace());
Element shares = fqlResponse.getChild("share_count", root.getNamespace());
Element clicks = fqlResponse.getChild("click_count", root.getNamespace());
Element total = fqlResponse.getChild("total_count", root.getNamespace());
int alikes = Integer.parseInt(likes.getText());
int ashares = Integer.parseInt(shares.getText());
int aclicks = Integer.parseInt(clicks.getText());
int atotal = Integer.parseInt(total.getText());
}
I just inserted "root.getNamespace()" when you call getChild and %20 & %22 in the query

Related

how to get specific tag content inside parant tag from id in xml using java?

I have an XML file as below. I want to get its specific child tag from the parent tag using java.
<?xml version="1.0"?>
<class>
<question id="scores">
<ans>12</ans>
<ans>32</ans>
<ans>44</ans>
</question>
<question id="ratings">
<ans>10</ans>
<ans>22</ans>
<ans>45</ans>
<ans>100</ans>
</question>
<default>
Sorry wrong
</default>
</class>
i want the function to be like this
String function(String id)
it will return the ans tag randomly
i.e if I give input id=scores, the program will look in the XML tag for scores as id and get length()of its children, in this case, 3, then retun randomly like 32 or 44 or 12.if id is not present, return default.
my code so far
public class ChatBot {
private String filepath="E:\\myfile.xml";
private File file;
private Document doc;
public ChatBot() throws SAXException, IOException, ParserConfigurationException {
file = new File("E:\\myfile.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(file);
}
String Function(String id){
// This part
return null;
}
}
As suggested by #LMC (because of org.w3c.dom.Document.getElementById() not recognizing arbitrary id attributes as IDs for getElementById() or as a browser would, mostly for HTML semantics/format), maybe:
String Function(String id) throws XPathExpressionException {
XPath xPath = XPathFactory.newInstance().newXPath();
// Be aware: id inserted without any escaping!
NodeList parents = (NodeList)xPath.evaluate("/class/question[#id='" + id + "']", doc, XPathConstants.NODESET);
if (parents.getLength() < 1) {
return null;
} else if (parents.getLength() > 1) {
// Huh, duplicates?
}
Element parent = (Element)parents.item(0);
NodeList children = parent.getChildNodes();
List<Element> answers = new ArrayList<Element>();
for (int i = 0, max = children.getLength(); i < max; i++) {
if (children.item(i).getNodeType() != Node.ELEMENT_NODE) {
continue;
}
if (children.item(i).getNodeName().equals("ans") != true) {
// Huh?
continue;
}
answers.add((Element)children.item(i));
}
if (answers.size() <= 0) {
return null;
}
int selection = (int)(Math.random() * answers.size());
return answers.get(selection).getTextContent();
}

Get node text with HTML task inside

I try to examine with Java XPath an html string like this:
<app>
<elem class="A">value1</elem>
<elem class="B">value2a<br />value2b</elem>
<elem class="C">value3</elem>
</app>
Actually for obtain the elem's value i use this code
public String getValue(String xml, String classValue){
XPath xpath = XPathFactory.newInstance().newXPath();
InputSource source = new InputSource(new StringReader(xml));
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
document = db.parse(source);
String xpathRequest = "//*[#class='"+classValue+"']/text()";
String value = xpath.evaluate(xpathRequest , document);
return value;
}
For classes A and C works fine, but when i ask the content of task with class B obtain only value2a
How i can get the complete string of node?
Simply run
String xpathRequest = "//*[#class='"+class+"']";
String value = this.xpath.evaluate(xpathRequest , document);
This will select the <elem> node and when converted to a String build the concatenation of all text content, e.g. Value2a Value2b
To get a list of all text contents below a Elem you need to select them as NodeSet:
String xpathRequest = "//*[#class='"+class+"']/text()";
NodeList textNodes = (NodeList)xpath.evaluate(xpathRequest , document, XPathConstants.NODESET);
ArrayList<String> texts = new ArrayList<>();
for (int i=0; i<textNodes.getLength(); i++)
texts.add(textNodes.item(i).getTextContent());
It is because xpath will return 2 value at this moment. Try below :-
List<WebElement> allprice = driver.findElements(By.xpath("//*[#class='B']/text()"));
for(WebElement a:WebElement allprice){
System.out.println(a.gettext());
}

Reading a complex XML Java

Thanks for considering this question.
I'm reading a complex XML file, which as you can see in the code has 44 main "nodes". Each node has further nested elements and so on.
I have managed to read the info from the first node but it seems that after the first iteration, returns only null. What could I be missing?
for (int i=0; i<nodeList.getLength(); i++){
log(String.valueOf(i));
Element flightInfo = (Element)nodeList.item(i);
NodeList flights = flightInfo.getElementsByTagName("flight");
Element flight = (Element)flights.item(0);
String flightId = flight.getAttribute("id");
String airlineCode = flight.getAttribute("airlineCode");
String operationType = flight.getAttribute("operationType");
String flightRoute= flight.getAttribute("flightType");
String scheduledTime = flight.getAttribute("scheduledTime");
NodeList routingList = flight.getElementsByTagName("routingList");
Element iatas = (Element)routingList.item(0);
NodeList _iata = (iatas.getElementsByTagName("IATA"));
String iata = _iata.item(i).getFirstChild().getNodeValue();
NodeList times = flight.getElementsByTagName("times");
Element realTimes = (Element)times.item(0);
NodeList _realTime = (realTimes.getElementsByTagName("realTime"));
String realTime = _realTime.item(0).getFirstChild().getNodeValue();
NodeList means = flight.getElementsByTagName("means");
Element gates = (Element)means.item(0);
NodeList _gate = gates.getElementsByTagName("gate");
Element gate = (Element)_gate.item(0);
String gateId = gate.getAttribute("id");
Element bagClaimList = (Element)means.item(0);
NodeList bagClaims = bagClaimList.getElementsByTagName("bagClaim");
Element bagClaim = (Element)bagClaims.item(0);
String bagId = bagClaim.getAttribute("id");
Element standList = (Element)means.item(0);
NodeList stands = standList.getElementsByTagName("stand");
Element _stand = (Element)stands.item(i);
String standId = _stand.getAttribute("id");
NodeList remarks = flight.getElementsByTagName("flight");
Element remarkCodes = (Element)remarks.item(0);
NodeList _remarkCode = (remarkCodes.getElementsByTagName("remarkCode"));
String remarkCode = _remarkCode.item(0).getFirstChild().getNodeValue();
flightList.add(new Flight(flightId, airlineCode, operationType,iata, scheduledTime, iata, realTime, gateId, bagId, standId, remarkCode));
log("Added new flightInfo");
}
The XML I'm reading is the following:
<flightData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file://c:/SITA/IKUSI FIDS/FIDS.XSD">
<flightInfo>
<flight id="AM2613" airlineCode="AM" flightNumber="2613" operationType="D" flightType="D" scheduledTime="2013-07-18T07:00:00">
<routingList>
<IATA>MTY</IATA>
</routingList>
<times>
<realTime>2013-07-18T07:00:00</realTime>
</times>
<means>
<gateList>
<gate id="N/14"/>
</gateList>
<bagClaimList>
<bagClaim id="2"/>
</bagClaimList>
<standList>
<stand id="5"/>
</standList>
</means>
<remarks>
<remarkCode>DEP</remarkCode>
</remarks>
</flight>
</flightInfo>
<flightInfo>...</flightInfo>
<flightInfo>...</flightInfo>
<flightInfo>...</flightInfo>
<flightInfo>...</flightInfo>
You'd be better off using JAXB: with the xsd file you will be able to generate java classes representing your model and won't have to write all this data extraction code.

Android - Reading XML issues

I've got an app that consumes a .NET web service which returns an XML string of data. I'm trying to read this XML and insert it into the local SQLite DB but I'm having some trouble. Here's a sample of the xml:
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="RemoteWebService"><OpenIssues> <Table> <IssueID>15351</IssueID> <IssueSummary>Computer keeps crashing. This continues to be a problem</IssueSummary> <LocationName>West Side</LocationName> <Status>WIP</Status> <CustomerID>89755</CustomerID> <CustomerName>West Side Computers</CustomerName> <CustomerShortName>WSC</CustomerShortName> <Notes /> <STATUS1>Work In Progress</STATUS1> <SubmittedBy>WSC - Tom Johns</SubmittedBy> <EQ_Replaced>true</EQ_Replaced></Table> </OpenIssues></string>
Using DOM, I'm trying to parse the results like so:
private void GetLatestData(String response) throws ParserConfigurationException, SAXException, IOException{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(response)));
//Normalize the document.
doc.getDocumentElement().normalize();
//Get Root Node.
NodeList nodeList = doc.getElementsByTagName("Table");
Node node = nodeList.item(0);
//Get Child Nodes.
for(int i = 0; i < node.getChildNodes().getLength(); i++){
IssueInfo issue = new IssueInfo();
Node tempNode = node.getChildNodes().item(i);
if(tempNode.getNodeName().equalsIgnoreCase("IssueID")){
issue.setIssueNumber(Long.parseLong(tempNode.getTextContent()));
}
if(tempNode.getNodeName().equalsIgnoreCase("IssueSummary")){
issue.setIssueNumber(Long.parseLong(tempNode.getTextContent()));
}
if(issue.getIssueNumber() > 0 && issue.getIssueSummary() != null){
creator = new IssueInfoCreator(this, DBVersion);
creator.open();
creator.InsertIssue(issue.getIssueNumber(), DateFormat.getDateInstance().format(new Date()), issue.getIssueSummary());
creator.close();
}
}
}
When I run it through the debugger, it gets "IssueID" just fine but how can I get it to pickup the next node "IssueSummary" right after that so I can insert the data at once? It seems like I need another loop somewhere, just not too sure where though.
If I understand your question correctly, this is what you may need to do.
Node node = nodeList.item(0);
Returns
<Table>
<IssueID>15351</IssueID>
<IssueSummary>Computer keeps crashing. This continues to be a problem</IssueSummary>
<Notes />
</Table>
node.getChildNodes().getLength();
Returns 3.
IssueInfo issue = new IssueInfo();
// Go through each child and find out node name and populate it.
for(int i = 0; i < node.getChildNodes().getLength(); i++){
Node tempNode = node.getChildNodes().item(i);
if(tempNode.getNodeName().equalsIgnoreCase("IssueID")){
issue.setIssueNumber(Long.parseLong(tempNode.getTextContent()));
}
if(tempNode.getNodeName().equalsIgnoreCase("IssueSummary")){
issue.setIssueNumber(Long.parseLong(tempNode.getTextContent()));
}
}
Move if logic out of the loop.
if(issue.getIssueNumber() > 0 && issue.getIssueSummary() != null){
creator = new IssueInfoCreator(this, DBVersion);
creator.open();
creator.InsertIssue(issue.getIssueNumber(), DateFormat.getDateInstance().format(new Date()), issue.getIssueSummary());
creator.close();
}
Seems like a simple DOM traversing issue.
If you can guarantee the next node is the summary, you could try using the getNextSibling() method for nodes
I modified your code so I could call it without using your classes. This is the code I used:
private static void GetLatestData(String response) {
try{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
/* the following 2 lines help you eliminate whitespace
from your xml DOM tree */
dbf.setValidating(true);
dbf.setIgnoringElementContentWhitespace(true);
DocumentBuilder db = dbf.newDocumentBuilder();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(response)));
//Normalize the document.
doc.getDocumentElement().normalize();
//Get Root Node.
NodeList nodeList = doc.getElementsByTagName("Table");
Node node = nodeList.item(0);
long issueNumber;
String summary;
//Get Child Nodes.
for(int i = 0; i < node.getChildNodes().getLength(); i++){
Node tempNode = node.getChildNodes().item(i);
if(tempNode.getNodeName().equalsIgnoreCase("IssueID")){
issueNumber = (Long.parseLong(tempNode.getTextContent()));
Node summaryNode = tempNode.getNextSibling();
summary = summaryNode.getTextContent();
System.out.println(String.format("Issue # %d, Summary: %s" , issueNumber,summary));
}
}
}catch(Exception exception){
exception.printStackTrace();
}
}
and I call it like this:
GetLatestData("<OpenIssues> " +
"<Table> " +
"<IssueID>15351</IssueID>" +
"<IssueSummary>Computer keeps crashing. This continues to be a problem</IssueSummary> " +
"<Notes />" +
"</Table></OpenIssues> ");
from a simple Java class. It's working all right for me, at least. It prints out:
Issue # 15351, Summary: Computer keeps crashing. This continues to be a problem
*smacks forehead*
issue will never have more than one value set, as it's created anew for each child node.
Just swap the two lines to create issue only once:
IssueInfo issue = new IssueInfo();
for(int i = 0; i < node.getChildNodes().getLength(); i++){
...
You should probably move the final if outside the for too, so it's not executed more than once.
AND you'll need to actually set the summary in the second if not. You're setting the 'issue number' twice.
Finally found the resolution to this with the help of my coworker and some digging around. It should be noted that we changed the WebService that returned a string from DataSet.GetXml() to an XmlDocument.InnerXml. This removed the spaces in between the nodes and then we were able to move forward from there. Here's the final code we used:
public void GetLatestData(SoapPrimitive xml)throws ParserConfigurationException, SAXException, IOException{
//get the factory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//Using factory get an instance of document builder
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc;
//parse using builder to get DOM representation of the XML file
InputSource is = new InputSource(new StringReader(xml.toString()));
doc = db.parse(is);
//Clear out Issues table first.
creator = new IssueInfoCreator(this, DBVersion);
creator.open();
creator.ClearIssueTable();
creator.close();
NodeList nodes = doc.getElementsByTagName("Table");
for(int i = 0; i < nodes.getLength(); i++) {
IssueInfo issue = new IssueInfo();
Element e = (Element)nodes.item(i);
issue.setIssueNumber(Long.parseLong(XMLfunctions.getValue(e, "IssueID")));
issue.setIssueSummary(XMLfunctions.getValue(e, "IssueSummary"));
issue.setDateReceived(DateFormat.format("MM/dd/yyyy hh:mm:ss", System.currentTimeMillis()).toString());
if(issue.getIssueNumber() > 0 && issue.getIssueSummary() != null){
creator = new IssueInfoCreator(this, DBVersion);
creator.open();
creator.InsertIssue(issue.getIssueNumber(), issue.getDateReceived(), issue.getIssueSummary());
creator.close();
}
}
}
And here is the getValue method of the XMLfuntions class:
public static String getValue(Element item, String str) {
NodeList n = item.getElementsByTagName(str);
return XMLfunctions.getElementValue(n.item(0));
}
public final static String getElementValue( Node elem ) {
Node kid;
if( elem != null){
if (elem.hasChildNodes()){
for( kid = elem.getFirstChild(); kid != null; kid = kid.getNextSibling() ){
if( kid.getNodeType() == Node.TEXT_NODE ){
return kid.getNodeValue();
}
}
}
}
return "";
}
Definately not taking credit for this, I found it here:
Programmer XR and modified it to my needs.
Hopefully this will help other people out!

Parsing xml string containing hyperlink

I am using DOM to parse an XML string as in the following example. This works great except in one instance. The document which I am trying to parse looks like this:
<response requestID=\"1234\">
<expectedValue>Alarm</expectedValue>
<recommendations>For steps on how to resolve visit Website and use the search features for \"Alarm\"<recommendations>
<setting>Active</setting>
<response>
The code I used to parse the XML is as follows:
try {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xmlResult));
Document doc = db.parse(is);
NodeList nlResponse = doc.getElementsByTagName("response");
String[] String = new String[3]; //result entries
for (int i = 0; i < nlResponse.getLength(); i++) {
Element e = (Element) nlResponse.item(i);
int c1 = 0; //count for string array
NodeList ev = e.getElementsByTagName("expectedValue");
Element line = (Element) ev.item(0);
String[c1] = (getCharacterDataFromElement(line));
c1++;
NodeList rec = e.getElementsByTagName("recommendations");
line = (Element) rec.item(0);
String[c1] = (getCharacterDataFromElement(line));
c1++;
NodeList set = e.getElementsByTagName("settings");
line = (Element) set.item(0);
String[c1] = (getCharacterDataFromElement(line));
c1++;
I am able to parse the code and put the result into a string array (as opposed to the System.out.println()). With the current code, my string array looks as follows:
String[0] = "Alarm"
String[1] = "For steps on how to resolve visit"
String[2] = "Active"
I would like some way of being able to read the rest of the information within "Recommendations" in order to ultimately display the hyperlink (along with other output) in a TextView. How can I do this?
I apologize for my previous answer in assuming your xml was ill-formed.
I think what is happening is that your call to the getCharacterDataFromElement is only looking at the first child node for text, when it will need to look at all the child nodes and getting the href attribute as well as the text for the 2nd child node when looking at the recommendations node.
e.g. after getting the Element for recommendation
String srec = "";
NodeList nl = line.getChildNodes();
srec += nl.item(0).getTextContent();
Node n = nl.item(1);
NamedNodeMap nm = n.getAttributes();
srec += "" + n.getTextContent() + "";
srec += nl.item(2).getTextContent();
String[c1] = srec;

Categories