[Qt-jambi-interest] Displaying the hierarchical structure of an xml file using model/view framework

moebius at altern.org moebius at altern.org
Mon Mar 10 17:28:25 CET 2008


Hello,

I'm trying to write a small app for displaying the hierarchical structure
of an xml file using the model/view framework.

To do this, I have created a XmlTreeModel class inherited from
QAbstractItemModel to hold the hierarchical model and a QTreeView to
display it. I have also defined a XmlTreeItem class to handle each single
xml element.
Parsing file and building model is achieved in populate(), parseBranch()
and createItem() functions. For each XmlTreeItem instance, the
corresponding QModelIndex.internalId is the reference identifier. This
Long value is stored into the XmlTreeItem instance itself and also into a
HashMap<Long, XmlTreeItem>. This unique identifier for each XmlTreeItem
instance is used to retrieve data once the model completely built,
especially in the QAbstractModelItem data() function.

Here is the model class code (quite long, sorry) :

public class XmlTreeModel extends QAbstractItemModel {
	private class XmlTreeItem{
		private Long id = null; // QModelIndex internal value ; null if not set
		private String name; // xml tag label
		private XmlTreeItem parentItem; // null for root item
		private List<XmlTreeItem> childItems = new ArrayList<XmlTreeItem>();

		public XmlTreeItem(String name, XmlTreeItem parent){
			parentItem = parent;
			this.name = name;
			childItems.clear();
		}

		public void appendChild(XmlTreeItem item) {
			childItems.add(item);
		}

		public XmlTreeItem child(int row) {
			return childItems.get(row);
		}

		public int childCount() {
			return childItems.size();
		}

		public int row() {
			if (parentItem != null)
				return parentItem.childItems.indexOf(this);

			return 0;
		}
	}

	private QDomDocument domDocument = new QDomDocument();
	private HashMap<Long, XmlTreeItem> indexes = new HashMap<Long,
XmlTreeItem>();
	private XmlTreeItem rootItem;
	private int nextId = 1;

	public XmlTreeModel(){
		super();
		indexes.clear();
	}

	public boolean populate(QIODevice device) {
		QDomDocument.Result res = domDocument.setContent(device, true);
		if (!res.success) {
			return false;
		}

		QDomElement rootElement = domDocument.documentElement();
		rootItem = createItem(rootElement, null);

		parseBranch(rootElement, rootItem);

		return true;
	}

	private void parseBranch(QDomElement parentElement, XmlTreeItem parentItem){
		QDomElement element = parentElement.firstChildElement();
		while(!element.isNull()){
			XmlTreeItem item = createItem(element, parentItem);
			parentItem.appendChild(item);
			if (!element.firstChildElement().isNull())
				parseBranch(element, item);
			element = element.nextSiblingElement();
		}
	}

	private XmlTreeItem createItem(QDomElement element, XmlTreeItem parentItem){
		XmlTreeItem item;
		if(parentItem == null)
			item = new XmlTreeItem(element.tagName(), null);
		else
			item = new XmlTreeItem(element.tagName(), parentItem);

		QModelIndex index = this.createIndex(item.childCount(), 0, nextId);
		indexes.put(index.internalId(), item);
		nextId++;

		item.id = index.internalId();

		return item;
	}

	private QModelIndex getIndex(XmlTreeItem item){
		if(item == null)
			return null;
		QModelIndex index = this.createIndex(item.row(), 0, item.id.intValue());
		return index;
	}

	private XmlTreeItem getItem(QModelIndex index){
		if (index == null)
			return rootItem;
		return indexes.get(index.internalId());
	}


	@Override
	public int columnCount(QModelIndex parent) {
		return 1;
	}

	@Override
	public Object data(QModelIndex index, int role) {
		if (role == Qt.ItemDataRole.DisplayRole){
			return indexes.get(index.internalId()).name;
		}
		return null;
	}

	@Override
	public QModelIndex index(int row, int column, QModelIndex parent) {
		if (parent == null)
			return getIndex(rootItem);

		XmlTreeItem item = getItem(parent).child(row);

		return this.createIndex(item.row(), 0, item.id.intValue());
	}

	@Override
	public QModelIndex parent(QModelIndex child) {
		if(child == null)
			return null;
		XmlTreeItem item = getItem(child).parentItem;
		return getIndex(item);
	}

	@Override
	public int rowCount(QModelIndex parent) {
		XmlTreeItem item = getItem(parent);
		return item.childItems.size();
	}
}

and here is the main/view class code:

public class XmlTreeView extends QWidget {

	public static void main(String[] args) {
		QApplication.initialize(args);
		new XmlTreeView();
		QApplication.exec();
	}

	public XmlTreeView() {
		QTreeView view = new QTreeView();
		String fileName = QFileDialog
				.getOpenFileName(view, "Open XML file", QDir.currentPath(),
						new QFileDialog.Filter("XML Files (*.xml)"));
		if (!fileName.isEmpty()) {
			XmlTreeModel model = new XmlTreeModel();
			model.populate(new QFile(fileName));
			view.header().hide();
			view.setWindowTitle("XMLTreeView: "+fileName);
			view.setModel(model);
			view.expandAll();
			view.show();
		}
	}
}


It works fine excepts when the root tag of my xml file has several
children i.e. the following file is correctly parsed and displayed:

<?xml version="1.0"?>
<root>
  <level1><!--only one level1 child-->
    <level2>
      <level3>
        <level4>
          <level5>100</level5>
        </level4>
      </level3>
      <level3>
        <level4>
          <level5>200</level5>
        </level4>
      </level3>
    </level2>
  </level1>
</root>

..altought this one is not:

<?xml version="1.0"?>
<root>
  <level1><!--first level1 child-->
    <level2>
      <level3>
        <level4>
          <level5>100</level5>
        </level4>
      </level3>
      <level3>
        <level4>
          <level5>200</level5>
        </level4>
      </level3>
    </level2>
  </level1>
  <level1><!--second level1 child-->
     various data...
  </level1>
</root>

Here, the root tag appears twice (both at the root level), and the second
occurrence contains a "fake" hierarchy that is more or less the same as
the first one (some minor variations may appear on some files).
Furthermore selection on this "phantom" hierarchy may cause application
crash.

I can't figure out why this problem occurs only at the first level,
especially because the parseBranch() function is recursive.

Any hint would really be very appreciated.

Thanks

jMax






More information about the Qt-jambi-interest mailing list