How to display XML in a JTree using JDOM
How to display XML in a JTree using JDOM
This brief tutorial will explain how to use Java to make and display a tree view of XML using JDOM and JTree. I don't claim this is original as most of the content is fromhere but
hopefully this will be easier to understand.
For this example I'm using the following configuration and jar files:
Java 1.5
JDOM 1.0
Apache commons-io 1.1
Java comes with a nice tree JTree that we will use. To put XML data
into the tree we will use the adapter pattern. This means we will wrap the XML data in wrappers that the JTree can understand and
work with. More specifically, we need to implement the TreeModel interface.
The TreeModel interface has the following methods:
public Object getRoot();
public Object getChild(Object parent, int index);
public int getChildCount(Object parent);
public boolean isLeaf(Object node);
public int getIndexOfChild(Object parent, Object child);
public void valueForPathChanged(TreePath path, Object newValue);
public void addTreeModelListener(TreeModelListener l);
public void removeTreeModelListener(TreeModelListener l); download source code
For this example we only need to worry about the first five since we won't be able to modify the underlying XML from the display. We need to create an "model adapter" that will use the TreeModel interface to
explain the JDOM format of XML. Here is the bare bones version of our model adapter:
public class JDOMTreeModelAdapter implements TreeModel {
//need to implement these
public Object getRoot(){};
public Object getChild(Object parent, int index){};
public int getChildCount(Object parent){};
public boolean isLeaf(Object node){};
public int getIndexOfChild(Object parent, Object child){};
// won't worry about these
public void valueForPathChanged(TreePath path, Object newValue){};
public void addTreeModelListener(TreeModelListener l){};
public void removeTreeModelListener(TreeModelListener l){};
}
In JDOM, an XML file is referred to as a Document.
Every Document contains a root Element,
which in turn contains other Elements. To make things easier, we will also create a "node adapter" as a kind of helper class for the model adapter. The node adapter will wrap a JDOM Element object, since this is the main class used to model data in JDOM. Here
is a basic node adapter (the version you can download below has additional functionality for display):
public class JDOMAdapterNode {
/** the Element encapsulated by this node */
public Element node;
/**
* Creates a new instance of the JDOMAdapterNode class
* @param Element node
*/
public JDOMAdapterNode(Element node) {
this.node = node;
}
/**
* Finds index of child in this node.
*
* @param child The child to look for
* @return index of child, -1 if not present (error)
*/
public int index(JDOMAdapterNode child) {
int count = childCount();
for (int i = 0; i < count; i++) {
JDOMAdapterNode n = this.child(i);
if (child.node == n.node) {
return i;
}
}
return -1; // Should never get here.
}
/**
* Returns an adapter node given a valid index found through
* the method: public int index(JDOMAdapterNode child)
*
* @param searchIndex find this by calling index(JDOMAdapterNode)
* @return the desired child
*/
public JDOMAdapterNode child(int searchIndex) {
Element child = (Element)node.getChildren().get(searchIndex);
return new JDOMAdapterNode(child);
}
/**
* Return the number of children for this element/node
*
* @return int number of children
*/
public int childCount() {
return node.getChildren().size();
}
}
download source code
Now that we have an adapter node it should be much easier to implement the TreeModel interface in our JDOMTreeModelAdapter. We need to add a constructor for passing in the JDOM Document for the tree to display.
public class JDOMToTreeModelAdapter implements TreeModel {
//JDOM Document to view as a tree
private Document document;
//listeners for changes, not used in this example
private ArrayList listenerList = new ArrayList();
//constructor used to set the document to view
public JDOMToTreeModelAdapter(Document doc) {
document = doc;
}
//override from TreeModel
public Object getRoot() {
if(document == null) return null;
return new JDOMAdapterNode(document.getRootElement());
}
//override from TreeModel
public Object getChild(Object parent, int index) {
JDOMAdapterNode node = (JDOMAdapterNode) parent;
return node.child(index);
}
//override from TreeModel
public int getIndexOfChild(Object parent, Object child) {
JDOMAdapterNode node = (JDOMAdapterNode) parent;
return node.index((JDOMAdapterNode) child);
}
//override from TreeModel
public int getChildCount(Object parent) {
JDOMAdapterNode jdomNode = (JDOMAdapterNode)parent;
return jdomNode.childCount();
}
//override from TreeModel
public boolean isLeaf(Object node) {
JDOMAdapterNode jdomNode = (JDOMAdapterNode)node;
return (jdomNode.node.getTextTrim().length() > 0);
}
//override from TreeModel
public void valueForPathChanged(TreePath path, Object newValue) {
// Null. We won't be making changes in the GUI
// If we did, we would ensure the new value was really new,
// adjust the model, and then fire a TreeNodesChanged event.
}
/*
* Use these methods to add and remove event listeners.
* (Needed to satisfy TreeModel interface, but not used.)
*/
// override from TreeModel
public void addTreeModelListener(TreeModelListener listener) {
if (listener != null && !listenerList.contains(listener)) {
listenerList.add(listener);
}
}
// override from TreeModel
public void removeTreeModelListener(TreeModelListener listener) {
if (listener != null) {
listenerList.remove(listener);
}
}
/*
* Invoke these methods to inform listeners of changes. (Not needed for this
* example.) Methods taken from TreeModelSupport class described at
* http://java.sun.com/products/jfc/tsc/articles/jtree/index.html That
* architecture (produced by Tom Santos and Steve Wilson) is more elegant. I
* just hacked 'em in here so they are immediately at hand.
*/
public void fireTreeNodesChanged(TreeModelEvent e) {
Iterator listeners = listenerList.iterator();
while (listeners.hasNext()) {
TreeModelListener listener = (TreeModelListener) listeners.next();
listener.treeNodesChanged(e);
}
}
public void fireTreeNodesInserted(TreeModelEvent e) {
Iterator listeners = listenerList.iterator();
while (listeners.hasNext()) {
TreeModelListener listener = (TreeModelListener) listeners.next();
listener.treeNodesInserted(e);
}
}
public void fireTreeNodesRemoved(TreeModelEvent e) {
Iterator listeners = listenerList.iterator();
while (listeners.hasNext()) {
TreeModelListener listener = (TreeModelListener) listeners.next();
listener.treeNodesRemoved(e);
}
}
public void fireTreeStructureChanged(TreeModelEvent e) {
Iterator listeners = listenerList.iterator();
while (listeners.hasNext()) {
TreeModelListener listener = (TreeModelListener) listeners.next();
listener.treeStructureChanged(e);
}
}
}
download source code
Now we need to create a Document from some raw xml to pass into our TreeModel adaptor for display. There are many choices for doing this and in this case we will use the JDOM
SAXBuilder. The SAXBuilder can take multiple forms of xml to build the Document. I prefer the Apache IOUtils. Here I'll create a new class that uses a SAXBuilder to create a JDOM Document from the xml source.
public class XMLTree {
//keep handles on the documents and readers
private static Document document;
private static SAXBuilder saxBuilder;
private static boolean validate = false;
private static BufferedReader reader;
private static byte[] xml = new byte[] {};
//tree to be displayed
private static JTree tree;
/**
* Creates a new instance of the JDOMTree class
*/
public XMLTree() {
saxBuilder = new SAXBuilder("org.apache.xerces.parsers.SAXParser", validate);
}
/**
* Returns the JTree with xml inside.
*
* @return JTree is present, or null.
*/
public JTree getTree() {
return tree;
}
/**
* Read in an XML file to display in the tree
*
* @param xmlFile Path to an XML file.
*/
public void parseFile(File xmlFile) throws Exception {
try {
//read file into a Document object
reader = new BufferedReader(new FileReader(xmlFile));
xml = IOUtils.toByteArray(reader);
try {
document = saxBuilder.build(new ByteArrayInputStream(xml));
//TODO later I'll add validation against the schema
//validate(document);
} catch (JDOMException jdome) {
throw new Exception("\n"+jdome.toString());
}
//convert the document object into a JTree
JDOMToTreeModelAdapter model = new JDOMToTreeModelAdapter(document);
tree = new JTree(model);
tree.setCellRenderer(new XMLTreeCellRenderer());
} catch (Exception e){
//if any exception set to null so we will
//refresh the display with the exception
tree = null;
throw e;
}
}
}
download source code
That's it for the internals. All we need now is a simple gui to test that this code really works. Here is a simple gui with a file chooser that allows the user to browse for the xml file to display. I am by
no means a gui programmer so this will be the bare bones.
public class XMLViewer extends JFrame {
private final String title = "JDOM XML Tree";
private final MenuBar menuBar = new MenuBar();
private final Menu fileMenu = new Menu();
private final MenuItem open = new MenuItem();
private final JFileChooser fileChooser = new JFileChooser();
private final XMLTree xmlTree;
private File file;
private JTree tree;
private Exception exception;
private final int windowHeight = 600;
private final int leftWidth = 380;
private final int rightWidth = 600;
private final int windowWidth = leftWidth + rightWidth;
private final Font treeFont = new Font("Lucida Console", Font.BOLD, 14);
private final Font textFont = new Font("Lucida Console", Font.PLAIN, 13);
/**
* Creates a simple gui for viewing xml in a tree.
*/
public XMLViewer() {
setTitle(getClass().getSimpleName());
setPreferredSize(new Dimension(windowWidth, windowHeight));
setFocusable(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
xmlTree = new XMLTree();
fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
fileChooser.setFileFilter(new XMLFileFilter());
fileChooser.setCurrentDirectory(new File("C:/"));
fileMenu.setLabel("File");
open.setLabel("Browse");
open.addActionListener(new MyActionListener());
makeFrame();
open.dispatchEvent(new ActionEvent(open,1001,open.getActionCommand()));
}
/**
* Construct a frame of the most recently read-in document.
*/
private void makeFrame() {
getContentPane().removeAll();
fileMenu.add(open);
menuBar.add(fileMenu);
setMenuBar(menuBar);
pack();
setVisible(true);
}
/**
* Displays the tree.
*
* @param tree JTree to display
*/
public void display() {
try {
makeFrame();
JScrollPane treeScrollPane = null;
JScrollPane textScrollPane = null;
// Build left-side view
if(tree != null) {
tree.setFont(treeFont);
treeScrollPane = new JScrollPane(tree);
treeScrollPane.setPreferredSize(new Dimension(leftWidth, windowHeight));
} else {
JEditorPane errorMessagePane = new JEditorPane();
errorMessagePane.setEditable(false);
errorMessagePane.setContentType("text/plain");
errorMessagePane.setText("Error: unable to build tree from xml:\n"+ exception.toString());
errorMessagePane.setCaretPosition(0);
treeScrollPane = new JScrollPane(errorMessagePane);
}
// Build right-side view
if(file != null) {
StringBuilder sb = new StringBuilder();
//TODO show validation
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = "";
while((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
reader.close();
} catch (Exception e) {
System.err.println("exception when reading file for display");
e.printStackTrace();
}
JEditorPane textPane = new JEditorPane();
textPane.setEditable(false);
textPane.setContentType("text/plain");
textPane.setText(sb.toString());
textPane.setCaretPosition(0);
textPane.setFont(textFont);
textScrollPane = new JScrollPane(textPane);
textScrollPane.setPreferredSize(new Dimension(rightWidth, windowHeight));
}
// Build split-pane view
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
treeScrollPane, textScrollPane);
splitPane.setContinuousLayout(true);
splitPane.setDividerLocation(leftWidth);
splitPane.setPreferredSize(new Dimension(windowWidth + 10,
windowHeight + 10));
// Add GUI components
setLayout(new BorderLayout());
add("Center", splitPane);
pack();
setVisible(true);
} catch (Exception e) {
System.err.println("error when updating xml viewer");
e.printStackTrace();
}
}
/** listener for when user selects a file to view */
private class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == open) {
int returnVal = fileChooser.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
//reset for currently selected message
exception = null;
file = fileChooser.getSelectedFile();
// update the gui for this file
setTitle(title + " | " + (file != null ? file.getAbsolutePath() : "Select A File"));
// remember last directory used
fileChooser.setCurrentDirectory(file);
try {
xmlTree.parseFile(file);
} catch (Exception e) {
exception = e;
}
tree = xmlTree.getTree();
display();
}
}
}
}
public static void main(String[] argv) {
new XMLViewer();
}
}
download source code
Even though the XMLViewer class contains a main method it won't compile for you. That's because I have yet to explain two additional classes that are used to make things easier. First off, it's really helpful
when browsing for files to display if your application is smart enough to hide non-xml file. This can be easily accomplished with a FileFilter,
so here is a simple FileFilter.
public class XMLFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String extension = getExtension(f);
if(extension != null) {
if(extension.equals("xml")) {
return true;
}
}
return false;
}
@Override
public String getDescription() {
return ".xml";
}
/**
* Get the lower case extension of a file.
*/
private String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i+1).toLowerCase();
}
return ext;
}
}
download source code
In addition to the FileFilter we can gain some display control of the JTree using an TreeCellRenderer.
The TreeCellRenderer will directly control how the xml is displayed inside the JTree. I will extend the DefaultTreeCellRenderer because
it already has some useful code.
public class XMLTreeCellRenderer extends DefaultTreeCellRenderer {
//colors for tree items
private final Color elementColor = new Color(0, 0, 128);
private final Color textColor = new Color(0, 128, 0);
//remove icons
public XMLTreeCellRenderer() {
setOpenIcon(new ImageIcon("open.gif"));
setClosedIcon(new ImageIcon("closed.gif"));
setLeafIcon(new ImageIcon("leaf.gif"));
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
JDOMAdapterNode adapterNode = (JDOMAdapterNode)value;
if(adapterNode.node.isRootElement()) {
value = adapterNode.node.getName();
} else if(adapterNode.node.getChildren().size() > 0) {
value = adapterNode.node.getName();
} else {
value = adapterNode.node.getName() +" ["+adapterNode.node.getTextTrim()+"]";
}
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if(!selected) {
if(adapterNode.node.getTextTrim().length() == 0) {
setForeground(elementColor);
} else {
setForeground(textColor);
}
}
return this;
}
}
download source code
Don't forget to use the .jar files mentioned at the top of this page as they are also required to run this code.
Enjoy!
How to display XML in a JTree using JDOM的更多相关文章
- Winform: use the WebBrowser to display XML with xslt, xml, xslt 转 html 字符串
原文:Winform: use the WebBrowser to display XML with xslt, xml, xslt 转 html 字符串 声明xml字符串: string xml = ...
- 【XML配置文件读取】使用jdom读取XML配置文件信息
在项目中我们经常需要将配置信息写在配置文件中,而XML配置文件是常用的格式. 下面将介绍如何通过jdom来读取xml配置文件信息. 配置文件信息 <?xml version="1.0& ...
- Java解析XML汇总(DOM/SAX/JDOM/DOM4j/XPath)
[目录] 一.[基础知识——扫盲] 二.[DOM.SAX.JDOM.DOM4j简单使用介绍] 三.[性能测试] 四.[对比] 五.[小插曲XPath] 六.[补充] 关键字:Java解析xml.解析x ...
- JavaEE XML的读写(利用JDom对XML文件进行读写)
1.有关XML的写 利用JDom2包,JDom2这个包中,至少引入org.jdom2.*;如果要进行XML文件的写出,则要进行导入org.jdom2.output.*; package com.lit ...
- xml解析技术
本文总结Dom,sax解析, 使用Java作为工具解析xml文档. 1 Dom 综述:Dom解析xml通常也称为xmlDom (和htmlDom技术差不多),将xml文档封装成树,好处就是xml中的 ...
- JAVA常用的XML解析方法
转并总结自(java xml) JAVA常用的解析xml的方法有四种,分别是DOM,JAX,JDOM,DOM4j xml文件 <?xml version="1.0" enco ...
- 四种生成和解析XML文档的方法详解(介绍+优缺点比较+示例)
众所周知,现在解析XML的方法越来越多,但主流的方法也就四种,即:DOM.SAX.JDOM和DOM4J 下面首先给出这四种方法的jar包下载地址 DOM:在现在的Java JDK里都自带了,在xml- ...
- Android 简易XML解析
首先创建在Android工程中创建一个Assets文件夹 app/src/main/assets 在这里添加一个名为 data.xml的文件,然后编辑这个文件,加入如下XML格式内容 <?xml ...
- xml解析方法总结
==========================================xml文件<?xml version=”1.0″ encoding=”GB2312″?> <RES ...
- xml 解析的四种方式
=========================================xml文件<?xml version="1.0" encoding="GB2312 ...
随机推荐
- ICMAN触摸滑条滚轮方案
ICMAN触摸滑条滚轮调光是一种利用触摸技术实现的调光控制方式,是一种更简单.直观且节能的调光方式,有效改善了用户的照明体验,并在智能家居和节能照明领域发挥着重要作用. 基于厦门晶尊微电子(ICMAN ...
- 在.net core使用Serilog,只要简单的三步
第一步:在项目上用nuget安装 Serilog.AspNetCore 最新的稳定版即可 ,安装这个会把其他需要的包都给包含着 第二步:修改 Program.cs 的 CreateHostBuilde ...
- 【YashanDB知识库】update/delete未选中行时,v$transaction视图没有事务,alter超时问题
问题现象 1.alter table修改表字段名,卡住,超时. 2.查看v$transaction事务视图,没有看到事务记录. 3.问题单:调整表结构时超时 问题风险及影响 无风险 问题影响版本 客户 ...
- ZEGO 教程 | RTC + AI 视觉的最佳实践(移动端)
摘要:帮助开发者在音视频场景中快速获得 AI 视觉功能 -- 美颜.滤镜.背景抠图等. 文|即构 Native SDK 开发团队 Z世代作为社会新的消费主力,追求个性.热爱新奇事物,青睐与酷炫 ...
- Go runtime 调度器精讲(七):案例分析
0. 前言 前面用了六讲介绍 Go runtime 调度器,这一讲我们看一个关于调度 goroutine 的程序案例分析下调度器做了什么.需要说明的是,这个程序和抢占有关,抢占目前为止还没有介绍到,如 ...
- 安卓Android虚拟机分享及使用
不知道大家伙在安装安卓虚拟机时被各式各样的问题折磨过没,我在安装安卓虚拟机时,遇到的问题简直就像长江之水源源不断,就算是最后安装好了也会因为各式各样的原因无法进入启动桌面. 当我发现这个可以直接导入到 ...
- 两小时学会使用dubbo(直接API、spring、注解、springboot)
最近上新的项目中需要用到dubbo,于是我决定温故知新,决定分享一下Dubbo在各种环境下的使用方式,本篇文章让你两小时就能学会使用dubbo 什么是Dubbo Dubbo是一个分布式.高性能.透明化 ...
- keycloak~token配置相关说明
会话有效期 在 Keycloak 中,"SSO Session Idle" 和 "SSO Session Max" 是用于配置单点登录(SSO)会话的两个参数. ...
- 创建一个专属的 CLI
作为一个前端,基本上每次初始化项目都会用到脚手架,通过一些脚手架可以快速的搭建一个前端的项目并集成一些所需的功能模块,避免自己每次都手动一个一个去安装.安装各个包的这个过程其实没啥营养,通过封装一个脚 ...
- JS函数:递归函数与迭代函数
1.递归函数 : 程序中调用自己的函数 程序调用自身的编程技巧称为 递归( recursion).递归作为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方 ...