Problem parsing XML document with Java SAX - java

I am parsing an XML document. I have done this thousands of times before, but I can't see why I am getting the following issue:
Here is the relevant part of the XML document that I am parsing:
XML: <?xml version="1.0" standalone="yes"?>
<ratings>
<url_template>http://api.netflix.com/users/T1BlCJtdcWMuF6gJEfue96_W.kZ_gW81h59KqLEfT1AzE-/ratings/title?{-join|&|title_refs}</url_template>
<ratings_item>
<user_rating value="not_interested"></user_rating>
<predicted_rating>4.8</predicted_rating>
<id>http://api.netflix.com/users/T1BlCJtdcWMuF6gJEfue96_W.kZ_gW81h59KqLEfT1AzE-/ratings/title/70112530</id>
<link href="http://api.netflix.com/catalog/titles/series/70112530/seasons/70112530" rel="http://schemas.netflix.com/catalog/title" title="Castle: Season 1">
</link>
.
.
.
So, I am trying to pase out the user_rating, the predicted_rating, and the id. I am doing this successfully. However, I am noticing that when user_rating contains no value, then the predicted_rating will automatically take the value of , rather than it's own value of 4.8. When user_rating does have value, however, then the predicted_rating will have the correct value. Here is my parsing code:
public class RatingsHandler extends DefaultHandler {
Vector vector;
Ratings ratings;
boolean inUserRating;
boolean inPredictedRating;
boolean inAverageRating;
boolean inID;
public void startDocument() throws SAXException {
vector = new Vector();
ratings = new Ratings();
}
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (localName.equals("user_rating")) {
inUserRating = true;
} else if (localName.equals("predicted_rating")) {
inPredictedRating = true;
} else if (localName.equals("average_rating")) {
inAverageRating = true;
} else if (localName.equals("id")) {
inID = true;
}
}
public void characters(char ch[], int start, int length)
throws SAXException {
if (inUserRating) {
ratings.setUserRating(new String(ch, start, length));
inUserRating = false;
} else if (inPredictedRating) {
ratings.setPredRating(new String(ch, start, length));
inPredictedRating = false;
} else if (inAverageRating) {
ratings.setAvgRating(new String(ch, start, length));
inAverageRating = false;
} else if (inID) {
Const.rating_id = new String(ch, start, length);
inID = false;
}
}
public void endDocument() throws SAXException {
if (ratings != null) {
vector.addElement(ratings);
}
}
public Vector getRatings() {
return vector;
}
}
Does it have something to do with the fact that user_rating has an attribute "value"? I would appreciate any help. Thanks!

I would suggest you to wait for the
endElement(String uri, String localName, String qName)
before you mark the element as passed by:
inSomething = false
I can imagine that when the element is empty, the
public void characters(char[] ch, int start, int length)
won't be called, your flag won't be cleared and you will run into inconsitent state having two inSomething flags set to true.

Related

How to parse and obtain a div element with a particular class using the SAX Parser

This is a snippet of my XML file
<name>Jason</name>
<div class="title">Domain Architect</div>
How do I parse using SAX Parser and obtain the div element with the particular class "title" ?
I am able to parse and obtain all the div elements.
Is there a simple way to obtain the elements that are of a particular class only.
EDIT :
This is how my handler is :
DefaultHandler handler = new DefaultHandler() {
boolean bfname = false;
boolean bdesig = false;
public void startElement(String uri, String localName,String qName,
Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("name")) {
bfname = true;
}
if((qName.equalsIgnoreCase("div"))) {
bdesig = true;
}
}
public void characters(char ch[], int start, int length) throws SAXException {
if (bfname) {
System.out.println("Name : " + new String(ch, start, length));
bfname = false;
}
if(bdesig) {
System.out.println("Designation : " + new String(ch, start, length));
bdesig = false;
}
}
};
In startElement() the class attribute can be evaluated from the attributes. You then could set a flag if there is an attribute class and you are interested in this particular class (its value). Reset the flag if you are not. Evaluate this flag in characters to retrieve the text contents of the element.
For small documents and non-critical performance you could use XPath to solve this in a more elegant way.

How to handle namespaces with SAX Parser?

I'm trying to learn to parse XML documents, I have a XML document that uses namespaces so, I'm sure I need to do something to parse correctly.
This is what I have:
DefaultHandler handler = new DefaultHandler() {
boolean bfname = false;
boolean blname = false;
boolean bnname = false;
boolean bsalary = false;
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
System.out.println("Start Element :" + qName);
if (qName.equalsIgnoreCase("FIRSTNAME")) {
bfname = true;
}
if (qName.equalsIgnoreCase("LASTNAME")) {
blname = true;
}
if (qName.equalsIgnoreCase("NICKNAME")) {
bnname = true;
}
if (qName.equalsIgnoreCase("SALARY")) {
bsalary = true;
}
}
public void endElement(String uri, String localName,
String qName) throws SAXException {
System.out.println("End Element :" + qName);
}
public void characters(char ch[], int start, int length) throws SAXException {
if (bfname) {
System.out.println("First Name : " + new String(ch, start, length));
bfname = false;
}
if (blname) {
System.out.println("Last Name : " + new String(ch, start, length));
blname = false;
}
if (bnname) {
System.out.println("Nick Name : " + new String(ch, start, length));
bnname = false;
}
if (bsalary) {
System.out.println("Salary : " + new String(ch, start, length));
bsalary = false;
}
}
};
saxParser.parse(file, handler);
My question is, how I can handle the namespase in this example?
To elaborate on what Blaise's point with sample code, consider this contrived example:
<?xml version="1.0" encoding="UTF-8"?>
<!-- ns.xml -->
<root xmlns:foo="http://data" xmlns="http://data">
<foo:record>ONE</foo:record>
<bar:record xmlns:bar="http://data">TWO</bar:record>
<record>THREE</record>
<record xmlns="http://metadata">meta 1</record>
<foo:record xmlns:foo="http://metadata">meta 2</foo:record>
</root>
There are two different types of record element. One in the http://data namespace; the other in http://metadata namespace. There are three data records and two metadata records.
The document could be normalized to this:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://data" xmlns:ns1="http://metadata">
<ns0:record>ONE</ns0:record>
<ns0:record>TWO</ns0:record>
<ns0:record>THREE</ns0:record>
<ns1:record>meta 1</ns1:record>
<ns1:record>meta 2</ns1:record>
</ns0:root>
But the code must handle the general case.
Here is some code for printing the metadata records:
class MetadataPrinter extends DefaultHandler {
private boolean isMeta = false;
#Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
isMeta = "http://metadata".equals(uri) && "record".equals(localName);
}
#Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if (isMeta) {
System.out.println();
isMeta = false;
}
}
#Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if (isMeta) {
System.out.print(new String(ch, start, length));
}
}
}
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
SAXParser parser = factory.newSAXParser();
parser.parse(new File("ns.xml"), new MetadataPrinter());
Note: namespace awareness must be enabled explicitly in some of the older Java XML APIs (SAX and DOM among them.)
In a namespace qualified XML document there are two components to a nodes name: namespace URI and local name (these are passed in as parameters to the startElement and endElement events). When you are checking for the presence of an element you should be matching on both these parameters. Currently your code would work for both documents below even though they are namespace qualified differently.
<foo xmlns="FOO">
<bar>Hello World</bar>
</foo>
And
<foo xmlns="BAR">
<bar>Hello World</bar>
</foo>
You are currently (and incorrectly) matching on the qName parameter. The problem with what you are doing is that the qName might change based on the prefix used to represent a namespace. The two documents below have the exact same namespace qualification. The local names and namespaces are the same, but their QNames are different.
<foo xmlns="FOO">
<bar>Hello World</bar>
</foo>
And
<ns:foo xmlns:ns="FOO">
<ns:bar>Hello World</ns:bar>
<ns:foo>

Getting SAX Parser attributes

<Details><propname key="workorderid">799</propname>
How do i get 799 from workorderid useing SAXParing?
when i use this code i get "workorderid" but not the value of workorderid
if(localName.equals("propname")){
String workid = attributes.getValue("key");
if(localName.equals("propname")){
//set one flag here and in endElement() get the value associated with your localname(propname)
String workid = attributes.getValue("key");
I am providing you the code try to understand and customize in your way.
public class ExampleHandler extends DefaultHandler {
private String item;
private boolean inItem = false;
private StringBuilder content;
public ExampleHandler() {
items = new Items();
content = new StringBuilder();
}
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
content = new StringBuilder();
if(localName.equalsIgnoreCase("propname")) {
inItem = true;
} else attributes.getValue("key");
}
public void endElement(String uri, String localName, String qName)
throws SAXException {
if(localName.equalsIgnoreCase("propname")) {
if(inItem) {
item = (content.toString());
}
}
public void characters(char[] ch, int start, int length)
throws SAXException {
content.append(ch, start, length);
}
public void endDocument() throws SAXException {
// you can do something here for example send
// the Channel object somewhere or whatever.
}
}
May somewhere wrong i'm in hurry. If helps Appreciate.
The following will hold the value of the node.
public void characters(char[] ch, int start, int length) throws SAXException {
tempVal = new String(ch,start,length);
}
In the event handler method, you need to get it like this:
if(qName.equals("propname")) {
System.out.println(" node value " + tempVal); // node value
String attr = attributes.getValue("key") ; // will return attribute value for the propname node.
}
In propname the attribute Key having value workorderid which is correct.
You need to get the value propname.
//Provide you tagname which is propname
NodeList nl = ele.getElementsByTagName(tagName);
if(nl != null && nl.getLength() > 0) {
Element el = (Element)nl.item(0);
textVal = el.getFirstChild().getNodeValue();
}

How can I get parent node while i using SAX parser?

I need to parse document using SAX parser in java. I was able to print all the node values if I use DefaultHandler class traditionally implementing the startElement, endElement and characters method. How can we access the the previous node value at child node, how can I do that?
My Sample XML is:
<staff>
<firstname>yong</firstname>
<lastname>mook kim</lastname>
<nickname>mkyong</nickname>
<salary>100000</salary>
</staff>
<staff>
<firstname>low</firstname>
<lastname>yin fong</lastname>
<nickname>fong fong</nickname>
<salary>200000</salary>
</staff>
Based on salary node value, I also want to access the first name. I am confused. How can we do it? My sample Code:
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
DefaultHandler handler = new DefaultHandler() {
boolean bfname = false;
boolean blname = false;
boolean bnname = false;
boolean bsalary = false;
public void startElement(String uri, String localName,String qName,
Attributes attributes) throws SAXException {
System.out.println("Start Element :" + qName);
if (qName.equalsIgnoreCase("FIRSTNAME")) {
bfname = true;
}
if (qName.equalsIgnoreCase("LASTNAME")) {
blname = true;
}
if (qName.equalsIgnoreCase("NICKNAME")) {
bnname = true;
}
if (qName.equalsIgnoreCase("SALARY")) {
bsalary = true;
}
}
public void endElement(String uri, String localName,
String qName) throws SAXException {
System.out.println("End Element :" + qName);
}
public void characters(char ch[], int start, int length) throws SAXException {
if (bfname) {
System.out.println("First Name : " + new String(ch, start, length));
bfname = false;
}
if (blname) {
System.out.println("Last Name : " + new String(ch, start, length));
blname = false;
}
if (bnname) {
System.out.println("Nick Name : " + new String(ch, start, length));
bnname = false;
}
if (bsalary) {
//System.out.println("Salary : " + new String(ch, start, length));
String nodeValue=new String(ch, start, length);
if(nodeValue.compareTo("100000")==0)
{
**????I need to store the respective respective first name
in ArrayList**
}
bsalary = false;
}
}
};
You can't navigate back and forth when using SAX. You should try using DOM. If you have to use SAX then you can use Stack to hold the previous data and pop them as required.
You can use a String variable to store the name as
public void characters(char ch[], int start, int length) throws SAXException {
... Code Here ...
if (bfname) {
employeeName = new String(ch, start, length);
bfname = false;
}
... Code Here ...
}
& use this variable at the end as
public void characters(char ch[], int start, int length) throws SAXException {
... Code Here ...
if (bsalary) {
String nodeValue=new String(ch, start, length);\
if(nodeValue.compareTo("100000")==0)
{
//Use employeeName Here...
}
bsalary = false;
}
... Code Here ...
}

parsing xml file from network database in android

i am trying to parse an xml file from an URL. I found an example in the following link
http://www.anddev.org/parsing_xml_from_the_net_-_using_the_saxparser-t353.html
and tried using it in my code but it returned the values to be as null
Following is my code of parsing xml
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
URL url = new URL("http://www.siva.com/search");
/** Handling XML */
SAXParserFactory saxparserfactory = SAXParserFactory.newInstance();
SAXParser saxparser = saxparserfactory.newSAXParser();
XMLReader xmlreader = saxparser.getXMLReader();
/* Create a new ContentHandler and apply it to the XML-Reader*/
ForListXMLHandler forlistmyhandler = new ForListXMLHandler();
xmlreader.setContentHandler(forlistmyhandler);
/* Parse the xml-data from our URL. */
xmlreader.parse(new InputSource(url.openStream()));
/* Parsing has finished. */
/* Our ExampleHandler now provides the parsed data to us. */
ParsedDataSet parsedDataSet = forlistmyhandler.getParsedData();
System.out.println(parsedDataSet.toString());
}
following is the code of MyXMLhandler
public class ForListXMLHandler extends DefaultHandler {
private boolean in_outertag = false;
private boolean in_innertag = false;
private boolean in_First_name = false;
private boolean in_Last_name = false;
private ParsedDataSet myParsedDataSet = new ParsedDataSet();
public ParsedDataSet getParsedData() {
return this.myParsedDataSet;
}
#Override
public void startDocument() throws SAXException {
this.myParsedDataSet = new ParsedDataSet();
}
#Override
public void endDocument() throws SAXException {
// Nothing to do
}
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
if (localName.equals("Searchdata")) {
this.in_outertag = true;
} else if (localName.equals("Searchdata")) {
this.in_innertag = true;
} else if (localName.equals("First_name")) {
this.in_First_name = true;
} else if (localName.equals("Last_name")) {
this.in_Last_name = true;
}
}
/**
* Gets be called on closing tags like:
* */
#Override
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
if (localName.equals("Searchdata")) {
this.in_outertag = false;
} else if (localName.equals("Searchdata")) {
this.in_innertag = false;
} else if (localName.equals("First_name")) {
this.in_First_name = false;
} else if (localName.equals("Last_name")) {
// Nothing to do here
}
}
/**
* Gets be called on the following structure: characters
*/
#Override
public void characters(char ch[], int start, int length) {
if (this.in_First_name) {
myParsedDataSet.setfirstname(new String(ch, start, length));
}
if (this.in_Last_name) {
myParsedDataSet.setlastname(new String(ch, start, length));
}
}
}
next part is of my parsed data set class
public class ParsedDataSet {
private String First_name = null;
private String Last_name = null;
public String getFirstname() {
return First_name;
}
public void setfirstname(String First_name) {
this.First_name = First_name;
}
public String getlastname() {
return Last_name;
}
public void setlastname(String Last_name) {
this.Last_name = Last_name;
}
public String toString() {
return this.First_name + "n" + this.Last_name;
}
}
pls tell me where i am getting error
The endElement method gets fired before the characters method, so your boolean variables are always set to false when the characters method gets fired. You should move some code from endElement to characters, something like this:
#Override
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
}
#Override
public void characters(char ch[], int start, int length) {
if (this.in_First_name) {
this.in_First_name = false;
myParsedDataSet.setfirstname(new String(ch, start, length));
}
if (this.in_Last_name) {
this.in_Last_name = false;
myParsedDataSet.setlastname(new String(ch, start, length));
}
}
You should also take a look here for a complete explanation on "Working with XML on Android".

Categories