I am working on parsing an XML file. I developed a swing application that reads data from xml file. It adds the chapters in comboBxCh
based on the subject value set in comboBxSb
.
The code is below:
Java code:
public void loadChapters(JComboBox comboBxCh, JComboBox comboBxSb)
{
tempList = xmlDoc.getElementsByTagName("subject");
NodeList secNodeList = null;
Element ele=null;
int i, j, totalElements;
totalElements = tempList.getLength();
i = j =0;
for(i=0;i<totalElements;i++)
{
secNodeList = tempList.item(i).getChildNodes();
for(j=0;j<secNodeList.getLength();j++)
{
if(secNodeList.item(j).getNodeType()==Node.ELEMENT_NODE)
{
ele = (Element)secNodeList.item(j);
}
if(ele!=null && ele.getTagName()=="sname" && ele.getTextContent()==comboBxSb.getSelectedItem().toString() )
{
for(i=0;i<secNodeList.getLength();i++)
{
if(secNodeList.item(i).getNodeType()==Node.ELEMENT_NODE)
ele = (Element)secNodeList.item(i);
if(ele.getTagName()=="chapter") //Adding it solves my problem-> &&i%2==1
{
comboBxCh.addItem(ele.getAttribute("name"));
}
}
return;
}
}
}
}
XML Code:
<subjects>
<subject id="1">
<sname>Quantitave Aptitude</sname>
<chapter name="Number series"/>
<chapter name="Time and work"/>
<chapter name="Trains and rivers"/>
</subject>
<subject id="2">
<sname>English</sname>
<chapter name="Articles"/>
<chapter name="Nouns"/>
<chapter name="Comprehension text"/>
</subject>
<subject id="3">
<sname>Logical Reasoning</sname>
<chapter name="What's next"/>
<chapter name="Next figure"/>
<chapter name="Series"/>
</subject>
<subject id="4">
<sname>GK</sname>
</subject>
</subjects>
When swing application loads then comboBxSb
contains all four subjects and Quantitative Aptitude (with id="1"
) remains selected in beginning hence its three chapters load in the comboBxCh
. But its each chapter loads twice! In xml file if I remove the \n
from between chapter names in each line then repetition doesn't happen. That is ->
<subject id="1">
<sname>Quantitave Aptitude</sname>
<chapter name="Number series"/><chapter name="Time and work"/><chapter name="Trains and rivers"/>
</subject>
. /-------------------------------------/
There is a screenshot of the application:
The solution to this, without modifying XML, is already mention in comment. That's I can add only odd(or even) no. of chapters. But my question is Why is this happening? It also happens if I put the names of chapters as text content between <chapter> </chapter>
tags and the extracting text with 'getTextContent()`
It's hard to be 100% sure, as you've only provided an out-of-context code snippet, but my "guess" is you've not cleared the JComboBox
of any previous values.
Having said that though, your loadChapters
method could be better done. Instead, you could make use of the xpath API to query the XML document and return only those values you actually want, like you might do for a database, for example...
public static List<String> loadChapters(Document xmlDoc, String name) throws XPathExpressionException {
List<String> results = new ArrayList<>(25);
XPathFactory xf = XPathFactory.newInstance();
XPath xPath = xf.newXPath();
String query = "/subjects/subject[sname[text()='" + name + "']]/chapter/@name";
XPathExpression xExp = xPath.compile(query);
NodeList nl = (NodeList) xExp.evaluate(xmlDoc, XPathConstants.NODESET);
for (int index = 0; index < nl.getLength(); index++) {
Node node = nl.item(index);
results.add(node.getNodeValue());
}
return results;
}
Using something like...
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Document doc = dbf.newDocumentBuilder().parse(new File("Test.xml"));
List<String> chapters = loadChapters(doc, "Quantitave Aptitude");
for (String chapter : chapters) {
System.out.println(chapter);
}
} catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException exp) {
exp.printStackTrace();
}
this prints...
Number series
Time and work
Trains and rivers
so we can ensure that the loadChapters
method is not returning duplicates.
Now to make your life easier, you could create a custom ComboBoxModel
which is backed by a List
....
public class ListComboBoxModel<E> extends AbstractListModel<E> implements MutableComboBoxModel<E> {
private List<E> values;
private Object selectedItem;
public ListComboBoxModel() {
this.values = new ArrayList<>(25);
}
public ListComboBoxModel(List<E> values) {
this.values = new ArrayList<>(values);
}
@Override
public int getSize() {
return values.size();
}
@Override
public E getElementAt(int index) {
return values.get(index);
}
@Override
public void addElement(E item) {
values.add(item);
}
@Override
public void removeElement(Object obj) {
values.remove((E)obj);
}
@Override
public void insertElementAt(E item, int index) {
values.add(index, item);
}
@Override
public void removeElementAt(int index) {
values.remove(index);
}
@Override
public void setSelectedItem(Object anItem) {
selectedItem = anItem;
}
@Override
public Object getSelectedItem() {
return selectedItem;
}
}
So you can create a new using something like...
ComboBoxModel<String> model = new ListComboBoxModel<String>(loadChapters(doc, "Quantitave Aptitude"));
and then apply the new model directly to the JComboBox