I need to parse the following XML structure in my Android activity. I have it in String format:
<Cube>
<Cube time="2012-09-20">
<Cube currency='USD' rate='1.2954'/>
<Cube currency='JPY' rate='101.21'/>
<!-- More cube tags here -->
</Cube>
</Cube>
Out of this I want to get an array of currency names (USD, JPY, etc) and their respective rates. Optionally, the date which appears only once in the XML document in the format specified above. Note the empty Cube tag also. There might be other weird occurrences like this as well. I only need to get Cube tags that have both currency and rate set.
Preferably using some XML parsing library and not regex, but if it resorts to that I am ready to use it as well.
Edit:
Here's what I've come up with so far. The problem is with inserting the matched elements inside an array, which I don't know how to do.
Pattern p = Pattern.compile("<Cube\\scurrency='(.*)'\\srate='(.*)'/>");
Matcher matcher = p.matcher(currency_source);
while (matcher.find()) {
Log.d("mine", matcher.group(1));
}
Here is a custom handler that should get the data you want:
public class MyHandler extends DefaultHandler {
private String time;
// I would use a simple data holder object which holds a pair
// name-value(or a HashMap)
private ArrayList<String> currencyName = new ArrayList<String>();
private ArrayList<String> currencyValue = new ArrayList<String>();
#Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (localName.equals("Cube")) { // it's a Cube!!!
// get the time
if (attributes.getIndex("", "time") != -1) {
// this Cube has the time!!!
time = attributes.getValue(attributes.getIndex("", "time"));
} else if (attributes.getIndex("", "currency") != -1
&& attributes.getIndex("", "rate") != -1) {
// this Cube has both the desired values so get them!!!
// but first see if both values are set
String name = attributes.getValue(attributes.getIndex("",
"currency"));
String value = attributes.getValue(attributes.getIndex("",
"rate"));
if (name != null && value != null) {
currencyName.add(name);
currencyName.add(value);
}
} else {
// this Cube doesn't have the time or both the desired values.
}
}
}
}
Then you could use it along http://developer.android.com/reference/android/util/Xml.html or one of the thousands tutorials out there to parse your xml String.
Related
This question already has answers here:
How to remove duplicates from a list?
(15 answers)
Closed 8 years ago.
I want to remove duplicates from ArrayList of type Alerts where Alerts is a class.
Class Alerts -
public class Alerts implements Parcelable {
String date = null;
String alertType = null;
String discription = null;
public Alerts() {
}
public Alerts(String date, String alertType, String discription) {
super();
this.date = date;
this.alertType = alertType;
this.discription = discription;
}
}
Here is how I added the elements -
ArrayList<Alerts> alert = new ArrayList<Alerts>();
Alerts obAlerts = new Alerts();
obAlerts = new Alerts();
obAlerts.date = Date1.toString();
obAlerts.alertType = "Alert Type 1";
obAlerts.discription = "Some Text";
alert.add(obAlerts);
obAlerts = new Alerts();
obAlerts.date = Date2.toString();
obAlerts.alertType = "Alert Type 1";
obAlerts.discription = "Some Text";
alert.add(obAlerts);
What I want to remove from them-
I want all alerts which have unique obAlerts.date and obAlerts.alertType. In other words, remove duplicate obAlerts.date and obAlerts.alertType alerts.
I tried this -
Alerts temp1, temp2;
String macTemp1, macTemp2, macDate1, macDate2;
for(int i=0;i<alert.size();i++)
{
temp1 = alert.get(i);
macTemp1=temp1.alertType.trim();
macDate1 = temp1.date.trim();
for(int j=i+1;j<alert.size();j++)
{
temp2 = alert.get(j);
macTemp2=temp2.alertType.trim();
macDate2 = temp2.date.trim();
if (macTemp2.equals(macTemp1) && macDate1.equals(macDate2))
{
alert.remove(temp2);
}
}
}
I also tried-
HashSet<Alerts> hs = new HashSet<Alerts>();
hs.addAll(obAlerts);
obAlerts.clear();
obAlerts.addAll(hs);
You need to specify yourself how the class decides equality by overriding a pair of methods:
public class Alert {
String date;
String alertType;
#Override
public boolean equals(Object o) {
if (this == 0) {
return true;
}
if ((o == null) || (!(o instanceof Alert)))
return false;
}
Alert alert = (Alert) o;
return this.date.equals(alert.date)
&& this.alertType.equals(alert.alertType);
}
#Override
public int hashCode() {
int dateHash;
int typeHash;
if (date == null) {
dateHash = super.hashCode();
} else {
dateHash = this.date.hashCode();
}
if (alertType == null) {
typeHash = super.hashCode();
} else {
typeHash = this.alertType.hashCode();
}
return dateHash + typeHash;
}
}
You can then loop through your ArrayList and add elements if they aren't already there as Collections.contains() makes use of these methods.
public List<Alert> getUniqueList(List<Alert> alertList) {
List<Alert> uniqueAlerts = new ArrayList<Alert>();
for (Alert alert : alertList) {
if (!uniqueAlerts.contains(alert)) {
uniqueAlerts.add(alert);
}
}
return uniqueAlerts;
}
However, after saying all that, you may want to revisit your design to use a Set or one of its family that doesn't allow duplicate elements. Depends on your project. Here's a comparison of Collections types
You could use a Set<>. By nature, Sets do no include duplicates. You just need to make sure that you have a proper hashCode() and equals() methods.
In your Alerts class, override the hashCode and equals methods to be dependent on the values of the fields you want to be primary keys. Afterwards, you can use a HashSet to store already seen instances while iterating over the ArrayList. When you find an instance which is not in the HashSet, add it to the HashSet, else remove it from the ArrayList. To make your life easier, you could switch to a HashSet altogether and be done with duplicates per se.
Beware that for overriding hashCode and equals, some constraints apply.
This thread has some helpful pointers on how to write good hashCode functions. An important lesson is that simply adding together all dependent fields' hashcodes is not sufficient because then swapping values between fields will lead to identical hashCodes which might not be desirable (compare swapping first name and last name). Instead, some sort of shifting-operation is usually done before adding the next atomic hash, eg. multiplying with a prime.
First store your datas in array then split at as one by one string,, till the length of that data execute arry and compare with acyual data by if condition and retun it,,
HashSet<String> hs = new HashSet<String>();
for(int i=0;i<alert.size();i++)
{
hs.add(alert.get(i).date + ","+ alert.get(i).alertType;
}
alert.clear();
String alertAll[] = null;
for (String s : hs) {
alertAll = s.split(",");
obAlerts = new Alerts();
obAlerts.date = alertAll[0];
obAlerts.alertType = alertAll[1];
alert.add(obAlerts);
}
I want to split a string and get a word finally. My data in database is as follows.
Mohandas Karamchand Gandhi (1869-1948), also known as Mahatma Gandhi, was born in Porbandar in the present day state of Gujarat in India on October 2, 1869.
He was raised in a very conservative family that had affiliations with the ruling family of Kathiawad. He was educated in law at University College, London.
src="/Leaders/gandhi.png"
From the above paragraph I want get the image name "gandhi". I am getting the index of "src=". But now how can I get the image name i.e "gandhi" finally.
My Code:
int index1;
public static String htmldata = "src=";
if(paragraph.contains("src="))
{
index1 = paragraph.indexOf(htmldata);
System.out.println("index1 val"+index1);
}
else
System.out.println("not found");
You can use the StringTokenizer class (from java.util package ):
StringTokenizer tokens = new StringTokenizer(CurrentString, ":");
String first = tokens.nextToken();// this will contain one word
String second = tokens.nextToken();// this will contain rhe other words
// in the case above I assumed the string has always that syntax (foo: bar)
// but you may want to check if there are tokens or not using the hasMoreTokens method
Try this code. Check if it working for you..
public String getString(String input)
{
Pattern pt = Pattern.compile("src=.*/(.*)\\..*");
Matcher mt = pt.matcher(input);
if(mt.find())
{
return mt.group(1);
}
return null;
}
Update:
Change for multiple item -
public ArrayList<String> getString(String input)
{
ArrayList<String> ret = new ArrayList<String>();
Pattern pt = Pattern.compile("src=.*/(.*)\\..*");
Matcher mt = pt.matcher(input);
while(mt.find())
{
ret.add(mt.group(1));
}
return ret;
}
Now you'll get an arraylist with all the name. If there is no name then you'll get an empty arraylist (size 0). Always make a check for size.
What I want to do...
I have a webview in my android app. I get a huge html content from the server as a string and a search string from the application user(the android phone user). Now I break the search string and create a regex out of it. I want all the html content that matches my regex to be highlighted when I display it into my WebView.
What I tried...
Since it is html, I just want to wrap the regex matched words into a pair of tags with yellow background.
Simple regex and replaceAll on the html Content that i get. Very wrong because it screws and replaces even what is inside the '<' and '>'.
I tried using Matcher and Pattern combo. It is difficult to omit what is inside the tags.
I used JSOUP Parser and it worked!
I traverse the html using NodeTraversor class. I used Matcher and Pattern classes to find and replace matched words with tags as i wanted to do.
But it is very slow. And I basically want to use it on Android and the size of it is like 284kB. I removed some unwanted classes and it is now 201kB but it is still too much for an android device. Additionally, the html content can be really large. I looked into JSoup source as well. It kind of iterates over every single character when it parses. I do not know whether all the parsers do the same but it is definitely slow for large html documents.
Here is my code -
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Highlighter {
private String regex;
private String htmlContent;
Pattern pat;
Matcher mat;
public Highlighter(String searchString, String htmlString) {
regex = buildRegexFromQuery(searchString);
htmlContent = htmlString;
pat = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
}
public String getHighlightedHtml() {
Document doc = Jsoup.parse(htmlContent);
final List<TextNode> nodesToChange = new ArrayList<TextNode>();
NodeTraversor nd = new NodeTraversor(new NodeVisitor() {
#Override
public void tail(Node node, int depth) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String text = textNode.getWholeText();
mat = pat.matcher(text);
if(mat.find()) {
nodesToChange.add(textNode);
}
}
}
#Override
public void head(Node node, int depth) {
}
});
nd.traverse(doc.body());
for (TextNode textNode : nodesToChange) {
Node newNode = buildElementForText(textNode);
textNode.replaceWith(newNode);
}
return doc.toString();
}
private static String buildRegexFromQuery(String queryString) {
String regex = "";
String queryToConvert = queryString;
/* Clean up query */
queryToConvert = queryToConvert.replaceAll("[\\p{Punct}]*", " ");
queryToConvert = queryToConvert.replaceAll("[\\s]*", " ");
String[] regexArray = queryString.split(" ");
regex = "(";
for(int i = 0; i < regexArray.length - 1; i++) {
String item = regexArray[i];
regex += "(\\b)" + item + "(\\b)|";
}
regex += "(\\b)" + regexArray[regexArray.length - 1] + "[a-zA-Z0-9]*?(\\b))";
return regex;
}
private Node buildElementForText(TextNode textNode) {
String text = textNode.getWholeText().trim();
ArrayList<MatchedWord> matchedWordSet = new ArrayList<MatchedWord>();
mat = pat.matcher(text);
while(mat.find()) {
matchedWordSet.add(new MatchedWord(mat.start(), mat.end()));
}
StringBuffer newText = new StringBuffer(text);
for(int i = matchedWordSet.size() - 1; i >= 0; i-- ) {
String wordToReplace = newText.substring(matchedWordSet.get(i).start, matchedWordSet.get(i).end);
wordToReplace = "<b>" + wordToReplace+ "</b>";
newText = newText.replace(matchedWordSet.get(i).start, matchedWordSet.get(i).end, wordToReplace);
}
return new DataNode(newText.toString(), textNode.baseUri());
}
class MatchedWord {
public int start;
public int end;
public MatchedWord(int start, int end) {
this.start = start;
this.end = end;
}
}
}
Here is how I call it -
htmlString = getHtmlFromServer();
Highlighter hl = new Highlighter("Hello World!", htmlString);
new htmlString = hl.getHighlightedHTML();
I am sure what i'm doing is not the most optimal way. But I can't seem to think of anything else.
I want to
- reduce the time it takes to highlight it.
- reduce the size of library
Any suggestions?
How about highlighting them using javascript?
You know, everybody love javascript, and you can find example like this blog.
JTidy and HTMLCleaner are aloso among the best Java HTML Parser.
see
Comparison between different Java HTML Parser
and
What are the pros and cons of the leading Java HTML parsers?
I am getting the most bizzarre behavior with trying to parse an XML, I run through it step by step and all values are assigned and retrieved in order and then the object I create is added to a HashMap for easy look up, the problem is when I am done retrieving it all the HashMap has null values and the ones that aren't null are the value of the very last node that was read, I have walked through it over and over and it all seems correct, but when it's done loading the values in the HasMap look like:
[0] null
[1] NarrationItem#44e9d170
[2] null
[3] null
[4] NarrationItem#44e9d170
etc, etc.
The format of my XML files is:
<narrations>
<narration id="0" name="A" alias="a" >
<line text="This is a test."></line>
</narration>
<narration id="1" name="B" alias="b" >
<line text="This another a test."></line>
</narration>
<narration id="2" name="C" alias="c" >
<line text="This is STILL a test."></line>
</narration>
</narrations>
And my XML parsing method is follows:
public HashMap<String, NarrationItem> NarrationMap = new HashMap<String, NarrationItem>();
private void LoadNarrationsXML() {
NarrationItem i = new NarrationItem();
String line;
String s;
try {
// Get the Android-specific compiled XML parser.
XmlResourceParser xmlParser = this.getResources().getXml(R.xml.narrations);
while (xmlParser.getEventType() != XmlResourceParser.END_DOCUMENT) {
if (xmlParser.getEventType() == XmlResourceParser.START_TAG) {
s = xmlParser.getName();
if (s.equals("narration")) {
i.Clear();
i.ID = xmlParser.getAttributeIntValue(null, "id", 0);
i.Name = xmlParser.getAttributeValue(null, "name");
i.Alias = xmlParser.getAttributeValue(null, "alias");
} else if (s.equals("line")) {
line = xmlParser.getAttributeValue(null, "text");
i.Narration.add(line);
}
} else if (xmlParser.getEventType() == XmlResourceParser.END_TAG) {
s = xmlParser.getName();
if (s.equals("narration")) {
NarrationMap.put(i.Alias, i);
}
}
xmlParser.next();
}
xmlParser.close();
} catch (XmlPullParserException xppe) {
Log.e(TAG, "Failure of .getEventType or .next, probably bad file format");
xppe.toString();
} catch (IOException ioe) {
Log.e(TAG, "Unable to read resource file");
ioe.printStackTrace();
}
}
The NarrationItem object is a custom object defined as:
public class NarrationItem {
int ID;
String Name;
String Alias;
ArrayList<String> Narration = new ArrayList<String>();
public NarrationItem() { }
public void LoadNarration(int id, String name, String alias, ArrayList<String> narration) {
ID = id;
Name = name;
Alias = alias;
Narration.addAll(narration);// = narration;
}
public void Clear() {
ID = 0;
Name = "";
Alias = "";
Narration.clear();
}
}//End Narration
If someone could point out the problem I'd be very thankful I have sat here staring at this issue for hours.
You're only ever creating one NarrationItem object - you're then using a reference to that object as the value for multiple entries in the map. Don't do that. You need to understand that the map doesn't contain an object as the value - it contains a reference to an object.
You can probably fix this just by creating a new NarrationItem each time instead of calling Clear.
It's not clear how you're looking at the map to see those null values, but if you're using the debugger and looking at the internal data structure, you probably shouldn't really be doing that either - instead, step through the keys, values or entries, i.e. stick within the abstraction that HashMap is meant to support.
I'm writing an app for android that needs to parse data from an XML file. I've never come across an error like this that is so impossibly hard to track down. Or maybe my brain just stopped working. That happens. XML file is of the form:
<?xml version="1.0" encoding="iso-8859-1"?>
<memberRoster>
<agent>
<agentInfo1>...</agentInfo1>
<agentInfo2>...</agentInfo2>
...
</agent>
<agent>
...
</agent>
...
</memberRoster>
So far it's working well, except for some random bits of fun!
Every now and then it will throw a NullPointerException. I did some more digging and found out that there are THREE "agents" (out of 800) with "supposedly" null data. I checked the XML file and the data is there, there are no illegal characters, etc. It is the same three "agents" every time. The program parses other entries before and after these "null" "agents". Also of note is that not all "agentInfo" fields in the ArrayList come up null; example, one of the entries has 7 of the 8 entries as null, with the 8th one non-null, another has only one null with the last 7 non-null.
I'm parsing the data in to an ArrayList from the XML file, and like I mentioned before, it works flawlessly until it comes to those three specific entries in the XML file.
I'm sorry I can't give much more info than that, the data is sensitive to our members.
EDIT:
Sorry! I knew I was forgetting something! :)
Some code from my XMLHandler.java class:
public void characters(char[] ch, int start, int length)
if(this.in_mr_agentNrdsId) {
agent[0] = ch.toString();
}
else if(this.in_mr_agentFirstName) {
agent[1] = ch.toString();
}
else if(this.in_mr_agentLastName) {
agent[2] = ch.toString();
}
else if(this.in_mr_agentPhone) {
agent[3] = ch.toString();
}
else if(this.in_mr_agentEmail) {
agent[4] = ch.toString();
}
else if(this.in_mr_agentOfficeName) {
agent[5] = ch.toString();
}
else if(this.in_mr_agentOfficePhone) {
agent[6] = ch.toString();
}
else if(this.in_mr_agentType) {
agent[7] = ch.toString();
pds.setMemberRoster(agent);
agent = new String[8];
}
PDS is an object of type ParsedDataSet, which is just a simple class containing the ArrayList objects and a few getter and setter methods:
public class ParsedDataSet {
private ArrayList agentOpenHouses = new ArrayList();
private ArrayList calendarOfEvents = new ArrayList();
private ArrayList latestStatistics = new ArrayList();
private ArrayList memberRoster = new ArrayList();
public ArrayList<String[]> getAgentOpenHouses() {
return agentOpenHouses;
}
public ArrayList<String[]> getCalendarOfEvents() {
return calendarOfEvents;
}
public ArrayList<String[]> getLatestStatistics() {
return latestStatistics;
}
public ArrayList<String[]> getMemberRoster() {
return memberRoster;
}
public void setAgentOpenHouses(String[] agentOpenHousesItem) {
this.agentOpenHouses.add(agentOpenHousesItem);
}
public void setCalendarOfEvents(String[] calendarOfEventsItem) {
this.calendarOfEvents.add(calendarOfEventsItem);
}
public void setLatestStatistics(String[] latestStatisticsItem) {
this.latestStatistics.add(latestStatisticsItem);
}
public void setMemberRoster(String[] memberRosterItem) {
this.memberRoster.add(memberRosterItem);
}
} // end class ParsedDataSet
You could throw an if statement into your assignements and reassign any caught 'NULL' or empty strings into a zero value or just reassign as variable = "" in your code.
For example:
if (agentInfo1 == NULL) {
agentInfo1 = "" || agentInfo1 = 0; //Depending on what your variables are
}
Try putting try catch loop in code to find where the error is happening, then, pinpoint the exact part of code that is giving this error, there do null checks before proceeding. This is based on best practices of software development, rather than a fix for you.
Alternatively, you can makes sure on server side that there are no "null" values, maybe by giving dummy value like "EMPTY_STRING". This is especially relevant if your app is already shipped and you cant make any client code changes.