xml|建立
我想您一定對XML有所瞭解,說不定您現在還躍躍欲試想寫一段XML文本呢,可是現在能找到的跨平台的、免費的XML編輯器太少了。所以在本文中,我想介紹一下或者說帶您一步一步的開發一個簡單的XML編輯器,當然我們要用到一些最常見的Java 2 Swing組件,不過這些都是免費的,有些是JDK中的,有些是可以從網上下載的。我想通過本文,你就可以建立一個屬於你自己的XML編輯器。
先讓我介紹一下本文輯寫的思路。首先我想簡要的討論一下XML和為什麼樹型結構比較適合用來顯示XML,然後我們來看一看JAXP API如何建立所需要的XML類的環境;然後我們將瞭解用來顯示一個圖形樹的JTree Swing組件;最後,我們將建立一個繼承JTree組件的可以重複使用的類,可以用來分析一個XML文檔,並把資料顯示在一個Jtree中。
說到XML(eXtensible Markup Languge),人們往往把它當成是一種新的用於Web瀏覽器中的標記語言,就象HTML或CSS一樣。其實,XML是一種資料表示語言,它允許你使用一種非常有效方法來描述你的資料。XML能夠使你定義諸如“these three words constitutes a heading”這樣的語句。XML允許你聲明任何類型的資料,而不是用來把這些資料顯示在網頁中。
請看一看下面的XML執行個體:
<article>
<header>
<title> 使用Java Swing 建立一個XML編輯器
<subtitle> 第一部分</subtitle>
</title>
<author> Wayne </author>
<header>
<content> 這是本文</content>
</article>
請注意,這些元素和標準的HTML語句是不同的,但是它們看上去比較象HTML,這是因為XML和HTML都是來源於SGML語言。不同的是HTML有預定義的標籤集,而XML的文法則有許多靈活性,它允許你使用表意的標記如<author>來括在資料兩邊。你還要注意,所有的元素都從屬於根項目(上例中為<article>),有些元素則還有自己的子項目,如<subtitle>就是<title>的子項目。這樣的資料群組織方式有三個好處:資料能夠更加表意,資料更加易維護而且資料更加容易作為一個樹的結構表現出來,這就是我們為什麼使用JTree對象來顯示XML資料的原因。如果你想對XML有更深的瞭解,請參閱天極網上的相關教程。
JAXP是一個用於處理XML的Java API,它能夠使應用程式分析並且轉化XML文檔,它的功能有點象JDBC API,都是把函數功能抽象成一個個方法。你可以去Apache網站找到最新的Xerces分析器,其中含有最新的JAXP,下載下來以後把它放在你的類目錄中。
下面讓我們看一下如何使用JTree Swing組件。
我們都知道,在自然界中,一棵樹通常都有一個非常粗的樹榦,樹榦上有許多樹枝分叉。每個樹杈和樹杈之間都有一定的聯絡,因為它們都有同一個來源:樹榦。這種繼承的關係並不只在樹枝中有,人類譜系也遵循相同的規律。從父母,到子女再到子女的子女,就這樣直到數不清為止。同樣,在資料存放區中,樹的概念也是一種使用同人類家譜樹一樣方法儲存資料的方法。樹的每一個樹杈稱為一個節點,每個有子節點的節點稱為父節點,所有的子節點的公用的父節點被稱為根節點。一個JTree組件就是一個簡單的樹資料結構的可視化表現形式。
幾乎所有的XML編輯器都包括一個可視化的樹結構,能讓你編輯XML文檔中的元素。我們馬上就會構建一個編輯器,不過在此之前,先讓我們再瞭解一下JTree組件。一個節點在一棵樹的某個位置儲存資料,為了儲存資料,必須知道任何一個父節點和它們的子節點。javax.swing.tree包定義了一些非常有用的介面,提供了一種通用的方法構建和操作一個樹結構。
TreeNode方法,用於訪問樹的節點的資訊
MutableTreeNode方法 用在一個可變的樹上(能夠添加或刪除子節點)
TreeModel方法 用於建立和管理與樹有關的資料模型。
接下來,我們將建立一個繼承JTree的類,提供分析XML文檔和用可視化JTree組件把節點顯示出來的功能。
建立XTree組件
XTree類由一個建構函式和三個方法組成,為了簡單起見我們的組件只能構建一個Xtree,在樹建立好之後不能進行處理它的節點。下面讓我們來看一個這個類。
域:
private DefaultMutableTreeNode treeNode 這個成員變數儲存TreeNode對象用於儲存JTree的模型。
DefaultMutableTreeNode類是在javax.swing.tree中被定義的,預設提供了MutableTreeNode介面的一個實現。
private DocumentBuilderFactory dbf
private DocumentBuilder db
private Document doc 這三個成員變數是JAXP的一部分,用來分析XML文本並轉化成DOM(Document Object Model) 對象。
建構函式
public XTree( String text )
這個建構函式通過使用傳送到構造器中的XML文本建立一個XTree對象。在初始化一些與JTree超類和DOM分析對象有關的基本顯示內容後,建構函式產生一個TreeModel 對象用來建立一個實際可視的樹。通過把DOM對象傳送到createTreeNode()方法來建立一個根節點,createTreeNode()方法返回一個DefaultMutableTreeNode類型的對象。這個對象然後被用來建立樹的TreeModel。
方法
private DefaultMutableTreeNode createTreeNode( Node root )
這個方法採用一個DOM 節點,然後在子節點中遞迴直到所有的接點都被添加到DefaultMutableTreeNode中。這是一個遞迴方法,為了找到根節點下的每一個子節點,它每次都要調用自己。JTree然後就可以使用DefaultMutableTreeNode對象了,因為它已經是樹型了。
private String getNodeType( Node node )
下面讓我們看一下如何使用JTree Swing組件。
我們都知道,在自然界中,一棵樹通常都有一個非常粗的樹榦,樹榦上有許多樹枝分叉。每個樹杈和樹杈之間都有一定的聯絡,因為它們都有同一個來源:樹榦。這種繼承的關係並不只在樹枝中有,人類譜系也遵循相同的規律。從父母,到子女再到子女的子女,就這樣直到數不清為止。同樣,在資料存放區中,樹的概念也是一種使用同人類家譜樹一樣方法儲存資料的方法。樹的每一個樹杈稱為一個節點,每個有子節點的節點稱為父節點,所有的子節點的公用的父節點被稱為根節點。一個JTree組件就是一個簡單的樹資料結構的可視化表現形式。
幾乎所有的XML編輯器都包括一個可視化的樹結構,能讓你編輯XML文檔中的元素。我們馬上就會構建一個編輯器,不過在此之前,先讓我們再瞭解一下JTree組件。一個節點在一棵樹的某個位置儲存資料,為了儲存資料,必須知道任何一個父節點和它們的子節點。javax.swing.tree包定義了一些非常有用的介面,提供了一種通用的方法構建和操作一個樹結構。
TreeNode方法,用於訪問樹的節點的資訊
MutableTreeNode方法 用在一個可變的樹上(能夠添加或刪除子節點)
TreeModel方法 用於建立和管理與樹有關的資料模型。
接下來,我們將建立一個繼承JTree的類,提供分析XML文檔和用可視化JTree組件把節點顯示出來的功能。
建立XTree組件
XTree類由一個建構函式和三個方法組成,為了簡單起見我們的組件只能構建一個Xtree,在樹建立好之後不能進行處理它的節點。下面讓我們來看一個這個類。
域:
private DefaultMutableTreeNode treeNode 這個成員變數儲存TreeNode對象用於儲存JTree的模型。
DefaultMutableTreeNode類是在javax.swing.tree中被定義的,預設提供了MutableTreeNode介面的一個實現。
private DocumentBuilderFactory dbf
private DocumentBuilder db
private Document doc 這三個成員變數是JAXP的一部分,用來分析XML文本並轉化成DOM(Document Object Model) 對象。
建構函式
public XTree( String text )
這個建構函式通過使用傳送到構造器中的XML文本建立一個XTree對象。在初始化一些與JTree超類和DOM分析對象有關的基本顯示內容後,建構函式產生一個TreeModel 對象用來建立一個實際可視的樹。通過把DOM對象傳送到createTreeNode()方法來建立一個根節點,createTreeNode()方法返回一個DefaultMutableTreeNode類型的對象。這個對象然後被用來建立樹的TreeModel。
方法
private DefaultMutableTreeNode createTreeNode( Node root )
這個方法採用一個DOM 節點,然後在子節點中遞迴直到所有的接點都被添加到DefaultMutableTreeNode中。這是一個遞迴方法,為了找到根節點下的每一個子節點,它每次都要調用自己。JTree然後就可以使用DefaultMutableTreeNode對象了,因為它已經是樹型了。
private String getNodeType( Node node )
這個方法,被createTreeNode()用來聯絡一個字串和某一種類型的節點。
private Node parseXml()
這個方法,用來分析XML文本字串,它返回Node類型的對象,能夠被傳送到createTreeNode()方法中。
下面我給出了java代碼,供大家分析研究。
// 到入W3C的DOM 類
import org.w3c.dom.*;
// JAXP的用於DOM I/O的類
import javax.xml.parsers.*;
// 標準Java類
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class XTree extends JTree
{
/**
* 這個成員變數儲存TreeNode對象用於儲存JTree的模型。
*DefaultMutableTreeNode類是在javax.swing.tree中被定義的
*預設提供了MutableTreeNode介面的一個實現。
*/
private DefaultMutableTreeNode treeNode;
/**
* 這三個成員變數是JAXP的一部分,用來分析XML文本並轉化成DOM(Document Object Model) 對象。
*/
private DocumentBuilderFactory dbf;
private DocumentBuilder db;
private Document doc;
/**
* 這個建構函式通過使用傳送到構造器中的XML文本建立一個XTree對象
* @參數 text是一個XML格式的XML文本
* @異常 ParserConfigurationException 如果建構函式非正常的設定分析器,就會拋出異常
*/
public XTree( String text ) throws ParserConfigurationException
{
super();
// 設定Tree渲染的基本屬性
getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION );
setShowsRootHandles( true );
setEditable( false ); // 允許樹可以編輯
// 通過初始化對象的DOM來分析對象
dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating( false );
db = dbf.newDocumentBuilder();
// 採用DOM根節點並且把它轉化成JTree的樹模型
treeNode = createTreeNode( parseXml( text ) );
setModel( new DefaultTreeModel( treeNode ) );
} file://中止XTree()
/**
* 這個方法採用一個DOM 節點,然後在子節點中遞迴直到所有的接點都被添加到DefaultMutableTreeNode中。
* 這是一個遞迴方法,為了找到根節點下的每一個子節點,它每次都要調用自己。
* JTree然後就可以使用DefaultMutableTreeNode對象了,因為它已經是樹型了。
*
* @參數 root org.w3c.Node.Node
*
* @傳回值 返回一個基於根節點DefaultMutableTreeNode對象
*/
private DefaultMutableTreeNode createTreeNode( Node root )
{
DefaultMutableTreeNode treeNode = null;
String type, name, value;
NamedNodeMap attribs;
Node attribNode;
// 從根節點中取得資料
type = getNodeType( root );
name = root.getNodeName();
value = root.getNodeValue();
treeNode = new DefaultMutableTreeNode( root.getNodeType() == Node.TEXT_NODE ? value : name );
// 顯示內容
attribs = root.getAttributes();
if( attribs != null )
{
for( int i = 0; i < attribs.getLength(); i++ )
{
attribNode = attribs.item(i);
name = attribNode.getNodeName().trim();
value = attribNode.getNodeValue().trim();
if ( value != null )
{
if ( value.length() > 0 )
{
treeNode.add( new DefaultMutableTreeNode( "[Attribute] --> " + name + "=\"" + value + "\"" ) );
} file://end if ( value.length() > 0 )
} file://end if ( value != null )
} file://end for( int i = 0; i < attribs.getLength(); i++ )
} file://end if( attribs != null )
// 如果存在子節點,遞迴
if( root.hasChildNodes() )
{
NodeList children;
int numChildren;
Node node;
String data;
children = root.getChildNodes();
// 如果子節點非空的話,只遞迴
if( children != null )
{
numChildren = children.getLength();
for (int i=0; i < numChildren; i++)
{
node = children.item(i);
if( node != null )
{
if( node.getNodeType() == Node.ELEMENT_NODE )
{
treeNode.add( createTreeNode(node) );
} file://end if( node.getNodeType() == Node.ELEMENT_NODE )
data = node.getNodeValue();
if( data != null )
{
data = data.trim();
if ( !data.equals("\n") && !data.equals("\r\n") && data.length() > 0 )
{
treeNode.add(createTreeNode(node));
} file://end if ( !data.equals("\n") && !data.equals("\r\n") && data.length() > 0 )
} file://end if( data != null )
} file://end if( node != null )
} file://end for (int i=0; i < numChildren; i++)
} file://end if( children != null )
} file://end if( root.hasChildNodes() )
return treeNode;
} file://end createTreeNode( Node root )
/**
* 這個方法,被createTreeNode()用來聯絡一個字串和某一種類型的節點。
*
* @參數 node org.w3c.Node.Node
*
* @傳回值 返回顯示節點類的字串
*/
private String getNodeType( Node node )
{
String type;
switch( node.getNodeType() )
{
case Node.ELEMENT_NODE:
{
type = "Element";
break;
}
case Node.ATTRIBUTE_NODE:
{
type = "Attribute";
break;
}
case Node.TEXT_NODE:
{
type = "Text";
break;
}
case Node.CDATA_SECTION_NODE:
{
type = "CData section";
break;
}
case Node.ENTITY_REFERENCE_NODE:
{
type = "Entity reference";
break;
}
case Node.ENTITY_NODE:
{
type = "Entity";
break;
}
case Node.PROCESSING_INSTRUCTION_NODE:
{
type = "Processing instruction";
break;
}
case Node.COMMENT_NODE:
{
type = "Comment";
break;
}
case Node.DOCUMENT_NODE:
{
type = "Document";
break;
}
case Node.DOCUMENT_TYPE_NODE:
{
type = "Document type";
break;
}
case Node.DOCUMENT_FRAGMENT_NODE:
{
type = "Document fragment";
break;
}
case Node.NOTATION_NODE:
{
type = "Notation";
break;
}
default:
{
type = "???";
break;
}
}// 結束 switch( node.getNodeType() )
return type;
} file://結束 getNodeType()
/**
* 這個方法,用來分析XML文本字串,它返回Node類型的對象,能夠被傳送到createTreeNode()方法中。
*
* @參數 text 一個顯示XML文檔的字串
* @傳回值 返回一個org.w3c.Node.Node對象
*/
private Node parseXml( String text )
{
ByteArrayInputStream byteStream;
byteStream = new ByteArrayInputStream( text.getBytes() );
try
{
doc = db.parse( byteStream );
}
catch ( Exception e )
{
e.printStackTrace();
System.exit(0);
}
return ( Node )doc.getDocumentElement();
} file://結束 parseXml()
} file://結束 class XTree
代碼2 XTreeTester.java
import javax.xml.parsers.*;
// GUI類
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
file://標準 Java類
import java.io.*;
public class XTreeTester extends JFrame
{
// XTree對象,用來在JTree中顯示XML
private XTree xTree;
// JScrollPane是JTree的容器
private JScrollPane jScroll;
private WindowListener winClosing;
// 設定架構的寬和高
private static final int FRAME_WIDTH = 400;
private static final int FRAME_HEIGHT = 300;
/*
* 構造器構造一個架構套件含JScrollPane,
* 把一個基於XML字串的XTree對象傳到建構函式中
*/
public XTreeTester( String title, String xml ) throws ParserConfigurationException
{
super( title );
Toolkit toolkit;
Dimension dim, minimumSize;
int screenHeight, screenWidth;
// 初始化基本的布局屬性
setBackground( Color.lightGray );
getContentPane().setLayout( new BorderLayout() );
toolkit = Toolkit.getDefaultToolkit();
dim = toolkit.getScreenSize();
screenHeight = dim.height;
screenWidth = dim.width;
setBounds( (screenWidth-FRAME_WIDTH)/2, (screenHeight-FRAME_HEIGHT)/2, FRAME_WIDTH, FRAME_HEIGHT );
// 構建XTree對象
xTree = new XTree( xml );
file://把XTree封裝到JScroll中,以便在JFrame可以使它在螢幕中上下滾動.
jScroll = new JScrollPane();
jScroll.getViewport().add( xTree );
// 添加捲軸到架構中
getContentPane().add( jScroll, BorderLayout.CENTER );
validate();
setVisible(true);
// 添加WindowListener用來關閉視窗
winClosing = new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
exit();
}
};
addWindowListener(winClosing);
}
// 程式從這裡開始執行。必須把一個以xml為副檔名的XML檔案傳送到這個方法中,其格式為java XTreeTester yourxmlfilename.xml
public static void main( String[] args )
{
String fileName = "";
BufferedReader reader;
String line;
StringBuffer xmlText;
XTreeTester xTreeTester;
// 建立一個基於特定XML檔案的文檔對象
try
{
if( args.length > 0 )
{
fileName = args[0];
if ( fileName.substring( fileName.indexOf( '.' ) ).equals( ".xml" ) )
{
reader = new BufferedReader( new FileReader( fileName ) );
xmlText = new StringBuffer();
while ( ( line = reader.readLine() ) != null )
{
xmlText.append( line );
}
// 分析完文檔對象後將重寫檔案
reader.close();
// 構造 GUI 組件
xTreeTester = new XTreeTester( "XTree 測試", xmlText.toString() );
}
else
{
help();
}
}
else
{
help();
}
}
catch( FileNotFoundException fnfEx )
{
System.out.println( "沒有發現"+ fileName + "檔案。" );
exit();
}
catch( Exception ex )
{
ex.printStackTrace();
exit();
}
}
file://協助資訊
private static void help()
{
System.out.println( "\n使用方法:java XTreeTester yourxmlfilename.xml" );
System.exit(0);
}
// 退出
private static void exit()
{
System.out.println( "\n謝謝使用 XTree" );
System.exit(0);
}
}