/******************************************
* 程序编写: 陈林茂
* 编写日期: 2003-03-16
* 联系作者: linmaochen@sohu.com
*******************************************/
众所周知,XML在现在的WEB应用程序中已非常炙手,但在程序中,我们如何很好的控制
XML中的数据,同时如何组合您的XML数据,确是一个比较麻烦的问题,下面的例子中我
将将我的一些解决办法介绍给大家:
1。首先,请大家看清下面的两个XML文件:
STRUCT.XML 为数据信息的具体结构描述;
Employees.xml : 则为具体的XML数据信息.
下面为具体的XML文本内容:
struct.xml:
<?xml version="1.0"?>
<Fieldset>
<Field>
<FieldName>EmpNo</FieldName>
<FieldType>1</FieldType>
<FieldSize>4</FieldSize>
</Field>
<Field>
<FieldName>FirstName</FieldName>
<FieldType>1</FieldType>
<FieldSize>8</FieldSize>
</Field>
<Field>
<FieldName>LastName</FieldName>
<FieldType>1</FieldType>
<FieldSize>8</FieldSize>
</Field>
<Field>
<FieldName>PhoneExt</FieldName>
<FieldType>1</FieldType>
<FieldSize>20</FieldSize>
</Field>
<Field>
<FieldName>HireDate</FieldName>
<FieldType>3</FieldType>
<FieldSize>10</FieldSize>
</Field>
<Field>
<FieldName>DeptNo</FieldName>
<FieldType>1</FieldType>
<FieldSize>10</FieldSize>
</Field>
<Field>
<FieldName>JobCode</FieldName>
<FieldType>1</FieldType>
<FieldSize>10</FieldSize>
</Field>
<Field>
<FieldName>JobGrade</FieldName>
<FieldType>1</FieldType>
<FieldSize>10</FieldSize>
</Field>
<Field>
<FieldName>JobCountry</FieldName>
<FieldType>1</FieldType>
<FieldSize>20</FieldSize>
</Field>
<Field>
<FieldName>Salary</FieldName>
<FieldType>4</FieldType>
<FieldSize>20</FieldSize>
</Field>
<Field>
<FieldName>FullName</FieldName>
<FieldType>1</FieldType>
<FieldSize>30</FieldSize>
</Field>
</Fieldset>
-----------------------------------------------------------------------------------
Employees.xml
<?xml version="1.0"?>
<RecordSet>
<Record>
<EmpNo>2</EmpNo>
<FirstName>Robert</FirstName>
<LastName>Nelson</LastName>
<PhoneExt>250</PhoneExt>
<HireDate>1988-12-28</HireDate>
<DeptNo>600</DeptNo>
<JobCode>VP</JobCode>
<JobGrade>2</JobGrade>
<JobCountry>USA</JobCountry>
<Salary>105900.000000</Salary>
<FullName>Nelson, Robert</FullName>
</Record>
<Record>
<EmpNo>4</EmpNo>
<FirstName>Bruce</FirstName>
<LastName>Young</LastName>
<PhoneExt>233</PhoneExt>
<HireDate>1988-12-28</HireDate>
<DeptNo>621</DeptNo>
<JobCode>Eng</JobCode>
<JobGrade>2</JobGrade>
<JobCountry>USA</JobCountry>
<Salary>97500.000000</Salary>
<FullName>Young, Bruce</FullName>
</Record>
<Record>
<EmpNo>5</EmpNo>
<FirstName>Kim</FirstName>
<LastName>Lambert</LastName>
<PhoneExt>22</PhoneExt>
<HireDate>1989-02-06</HireDate>
<DeptNo>130</DeptNo>
<JobCode>Eng</JobCode>
<JobGrade>2</JobGrade>
<JobCountry>USA</JobCountry>
<Salary>102750.000000</Salary>
<FullName>Lambert, Kim</FullName>
</Record>
</RecordSet>
2.为了很好的并且很直观的操纵XML的数据,我们将模拟一个类似于DELPHI中的QUERY 一样的组件,由于篇幅,
这里只提供它的简单实现:
它将包括三个基本的JAVA 类:
XFIELD.JAVA 单个字段信息的描述类;
XFIELDS.JAVA 单条记录信息的描述类;
XQUERY.JAVA 类似于QUERY 一样的查询类的控件.下面为三个类具体实现的源代码:
/**
* <p>Title: 字段单元</p>
* <p>Description: 内存记录中字段信息描述</p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: </p>
* @author 陈林茂 2003-03-12
* @version 1.0
*/
import java.lang.*;
import java.lang.Double;
/*
* 描述内存记录中的字段信息
*/
/*******************************************************
* log:
* 2003-03-12 : add XField
********************************************************/
public class XField {
/*
* 字段类型说明:
* ftInteger : 整数字段
* ftString : 字符串字段
* ftDate : 日期型字段
* ftFloat : 浮点型字段信息
*/
private static int ftString = 1;
private static int ftInteger = 2;
private static int ftDate = 3;
private static int ftFloat = 4;
/**************************************/
private String FieldName;
private int FieldType;
private String FieldValue;
private int FieldSize = 0;
public XField() {
}
public XField(String fldName){
FieldName = fldName;
}
public XField(String fldName,int fldType){
FieldName = fldName;
FieldType = fldType;
}
public XField(String fldName,int fldType,String fldValue){
FieldName = fldName;
FieldType = fldType;
FieldValue = fldValue;
}
//获取字段的名称
public String getFieldName(){
return this.FieldName ;
}
//获取字段的类别
public int getFieldType(){
return FieldType;
}
//设置字段的类别
public void setFieldType(int sFieldType){
this.FieldType = sFieldType ;
}
//设置字段的长度信息
public void setFieldSize(int sSize){
this.FieldSize = sSize ;
}
//获取当前字段的整型值
public int getAsInteger(){
if(IsInteger(FieldValue))
return Integer.parseInt(FieldValue);
return 0;
}
//获取当前字段的字符串值
public String getAsString(){
return FieldValue;
}
//获取当前字段的浮点型值
public double getAsFloat(){
if(IsFloat(FieldValue)){
return Double.parseDouble(FieldValue);
}
return 0.0;
}
//设置字段的值
public void setFieldValue(String sFldValue){
this.FieldValue = sFldValue;
}
/*
* 判断一字段值是否为整数
*/
private boolean IsInteger(String numStr){
int i=0;
for(i = 1;i < numStr.length(); i++){
if((numStr.charAt(i) <='0') || (numStr.charAt(i) >= '9' )){
return false;
}
}
return true;
}
/*
* 判断一字段值是否为浮点数
*/
private boolean IsFloat(String numStr){
int i=0;
for(i = 1;i < numStr.length(); i++){
if (numStr.charAt(i) != '.'){
if((numStr.charAt(i) <='0') || (numStr.charAt(i) >= '9' )){
return false;
}
}
}
return true;
}
/*
* 判断一字段值是否为日期型
*/
private boolean IsDateTime(String dateStr){
//to do
return true;
}
}
/**
* <p>Title: 内存单个记录信息类</p>
* <p>Description: 内存记录单个记录信息描述</p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: </p>
* @author 陈林茂 2003-03-13
* @version 1.0
*/
import java.util.*;
import java.lang.*;
import excelsample.XField;
/*
* XFields 类是将一些字段信息组合在一起,
* 从而组合成一个单条记录信息
* 它提供如下的功能:
* 1: 查找某一个字段信息
* 2: 重新设置某个字段的值
*/
public class XFields {
//字段信息容器
private Vector vector = new Vector(100);
public XFields() {
}
//添加一字段信息
public void AddXField(XField sFld){
vector.add(sFld);
}
//移去一字段信息
public void RemoveXField(XField sFld){
if(vector.size()>0)
vector.remove(sFld);
}
//返回记录中字段的个数
public int getCount(){
return vector.size() ;
}
//根据名称获取某个字段信息
public XField getFieldByname(String sFldName){
int i;
int pos ;
XField xfield;
for(i=0; i<vector.size(); i++){
xfield = (XField)(vector.elementAt(i));
System.out.println("field name is :"+xfield.getFieldName());
pos =xfield.getFieldName().toUpperCase().indexOf(sFldName.toUpperCase());
if(pos >= 0)
{
return xfield;
}
}
return null;
}
//根据序号获取某个字段信息
public XField getField(int Index){
if(vector.size() > Index)
return (XField)(vector.elementAt(Index));
return null;
}
//复制某一个字段信息
public XField CopyField(int index){
XField sfield;
if(this.vector.size() > index){
sfield =(XField)(vector.elementAt(index));
XField xfield =new XField(sfield.getFieldName(),
sfield.getFieldType(),
sfield.getAsString());
return xfield;
}
return null;
}
}
/**
* <p>Title: 内存记录集</p>
* <p>Description: 将XML文件中提供的记录信息存放到内存中</p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: </p>
* @author 陈林茂 2003-03-15
* @version 1.0
*/
/*
* 类的名称:XQuery
* 说明: 此类将模拟一个类似DELPHI QUERY 的结构信息;
* 1。 它提供记录的查找,移到第一条记录,移到下一条记录等操作;
* 2。 它同时包含结构及数据信息
* 3。 利用它可以方便地对内存中的数据进行检索
*/
import java.lang.*;
import java.util.*;
public class XQuery {
//内存记录容器
private Vector vector = new Vector(100);
//内存记录的结构信息
private XFields Struct = null;
//是否到记录的结尾
private boolean RecEof = true;
//当前操作的记录
private XFields CurrXFields = null;
//当前的记录编号
private int RecNo = 0;
public XQuery() {
}
//设置内存记录的结构信息
public void setSturcture(XFields sStruct){
this.Struct = sStruct;
}
//添加一记录信息
public void AddRecord(XFields xfields){
vector.add(xfields);
}
//移走一记录信息
public void RemoveRecord(XFields xfields){
if(vector.size() > 0 )
vector.remove(xfields);
}
//获取当前记录集的记录总数
public int getRecordCount(){
return vector.size();
}
/*
* 记录集的相关操作函数
*/
//移到第一条记录
public void First(){
if(vector.size()<=0){
this.RecEof = true;
return ;
}
CurrXFields = (XFields)(vector.elementAt(0));
this.RecEof = false;
this.RecNo = 0;
}
//移到下一条记录
public void Next(){
if(vector.size() == (this.RecNo+1)){
this.RecEof = true;
this.CurrXFields = null;
return ;
}
this.RecNo = this.RecNo + 1;
this.CurrXFields = (XFields)(vector.elementAt(this.RecNo));
this.RecEof =false;
}
//新增一空白记录信息
public void insertRecord(){
int i=0;
XFields xfields = new XFields();
for(i=0;i<this.Struct.getCount();i++){
xfields.AddXField(this.Struct.CopyField(i));
}
this.vector.add(xfields);
this.RecNo = this.vector.size();
this.CurrXFields = xfields;
System.out.println("insert a record!");
}
//获取记录集中的字段个数
public int getFieldsCount(){
return this.Struct.getCount() ;
}
//返回记录集的当前记录
public XFields RecordSet(){
return this.CurrXFields ;
}
//判断记录集是否到结尾
public boolean Eof(){
return this.RecEof ;
}
}
3.最后,就是告诉大家如何如何将XML中的数据读取出来,并放到XQUERY类中去,请看具体的实现:
(1) 建立一个解析XML文件的类(详见JBUILDER 中自带的示例):
import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
public class xmlParser extends DefaultHandler{
private static int INDENT = 4;
private static String attList = "";
//记录表信息
public static XQuery query =new XQuery();
//记录的结构信息
public XFields xfields=new XFields();
//临时的字段信息
private XField xfield = null;
private String elementName = "";
/*解析行为:
* 0: 解析XML文件中的信息为记录表的结构定义信息
* 1: 解析XML文件中的信息为记录表的具体的记录信息
*/
private static int parseMode =0;
private int idx = 0;
public void characters(char[] ch, int start, int length) throws SAXException {
String s = new String(ch, start, length);
if (ch[0] == '\n')
return;
System.out.println(getIndent() + " Value: " + s);
if(this.parseMode == 0){
if(this.elementName.indexOf("FieldName") >= 0) {
this.xfield = new XField(s);
System.out.println("Fieldname is :"+this.xfield.getFieldName());
}
if(this.elementName.indexOf("FieldType") >= 0){
if(this.xfield != null) {
try{
this.xfield.setFieldType(Integer.parseInt(s));
System.out.println("Fieldtype is:"+this.xfield.getFieldType());
}catch(Exception e){
e.printStackTrace();
}
}
}
if(this.elementName.indexOf("FieldSize") >= 0) {
if(this.xfield != null){
this.xfield.setFieldSize(Integer.parseInt(s)) ;
//添加一字段定义
this.xfields.AddXField(this.xfield);
System.out.println("record fields count is :" +this.xfields.getCount());
}
}
}
//
if(this.parseMode == 1) {
this.query.RecordSet().getFieldByname(this.elementName).setFieldValue(s);
}
}
public void endDocument() throws SAXException {
idx -= INDENT;
System.out.println(getIndent() + "end document");
System.out.println("...PARSING ends");
if(this.parseMode ==0)
this.query.setSturcture(this.xfields);
}
public void endElement(String uri, String localName, String qName) throws SAXException {
if (!attList.equals(""))
System.out.println(getIndent() + " Attributes: " + attList);
attList = "";
System.out.println(getIndent() + "end document");
idx -= INDENT;
System.out.println("the idx is :"+String.valueOf(idx));
}
public void startDocument() throws SAXException {
idx = 0;
idx += INDENT;
System.out.println("PARSING begins...");
System.out.println(getIndent() + "start document: ");
}
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
idx += INDENT;
System.out.println('\n' + getIndent() + "start element: " + localName);
if (attributes.getLength() > 0) {
idx += INDENT;
for (int i = 0; i < attributes.getLength(); i++) {
attList = attList + attributes.getLocalName(i) + " = " + attributes.getValue(i);
if (i < (attributes.getLength() - 1))
attList = attList + ", ";
}
idx-= INDENT;
}
//判断是否为具体信息层
switch(this.parseMode){
case 0:
this.elementName = localName;
break;
case 1:
this.elementName = localName ;
if(this.elementName.indexOf("Record") >= 0)
if(idx == 12){
this.query.insertRecord();
}
break;
}
}
private String getIndent() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < idx; i++)
sb.append(" ");
return sb.toString();
}
public static void setQuery(XQuery sxquery){
query = sxquery;
}
//设置识别模式
public static void setMode(int sMode){
parseMode = sMode;
}
}
(2) 如何从XML中读取数据出来,并放到XQUERY去,并且自由的操作数据:
/**
* <p>Title: Excel 文件信息写入类</p>
* <p>Description: 从一个XML文件中读取信息并写入到EXCEL文件中去</p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: </p>
* @author 陈林茂 2003-03-15
* @version 1.0
*/
/*
* EXCEL记录写入演示
* 说明: 它主要分三个步骤:
* 1 读取STRUCT.XML文件,建立记录的字段结构信息;
* 2 读取RECORD.XML文件,获取具体的记录信息;
* 3 根据记录信息,将信息写入EXCEL文件
*/
import java.io.File;
import java.util.Date;
import jxl.*; //为EXCEL开源支持类库
import jxl.write.*;
import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
public class ExcelWrite {
public ExcelWrite() {
}
public static void main(String argv[]){
if(argv.length != 3){
System.out.println("The Application argument is "
+"java ExcelWrite [struct.xml] [record.xml] [output.xls]");
System.exit(-1);
}
String StructFile = argv[0];
String RecordFile = argv[1];
String outputFile = argv[2];
int i = 0;
int m = 0;
XQuery sQuery =new XQuery();
//建立内存记录信息
try {
//建立XML解析器
XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
xmlParser MySaxParserInstance = new xmlParser();
xmlParser.setQuery(sQuery);
parser.setContentHandler(MySaxParserInstance);
//解析记录集的结构信息
xmlParser.setMode(0);
parser.parse(StructFile);
System.out.println("the recordset fields count is :"+sQuery.getFieldsCount());
//解析记录集的数据信息
xmlParser.setMode(1);
parser.parse(RecordFile);
System.out.println("the recordset record count is :"+sQuery.getRecordCount());
}
catch(IOException ioe) {
ioe.printStackTrace();
}
catch(SAXException saxe) {
saxe.printStackTrace();
}
//开始写入数据信息
try{
//建立一EXCEL文件写入类
WritableWorkbook workbook = Workbook.createWorkbook(new File(outputFile));
//建立一工作薄
WritableSheet sheet = workbook.createSheet("员工记录数据", 0);
//建立每列的标题信息,具体操纵XQUERY中的数据
String s = "";
for(i = 0; i<sQuery.getFieldsCount(); i++){
s = sQuery.RecordSet().getField(i).getFieldName();
Label label = new Label(i, 0, s);
sheet.addCell(label);
}
//依次填入记录信息
m = 1 ;
sQuery.First();
while (sQuery.Eof()==false){
for(i=0; i<sQuery.getFieldsCount(); i++){
s = sQuery.RecordSet().getField(i).getAsString();
Label label = new Label(i, m, s);
sheet.addCell(label);
}
sQuery.Next();
m = m + 1;
}
//write the data to File
workbook.write();
workbook.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
4.总结:
首先建立记录的原始记录信息,分成两个XML文件,其中一个为数据具体的结构描述,
另外一个为具体的数据信息;
然后建立一个类似QUERY的类,可以方便地控制内存中的数据信息(包括记录的上移 下移等);
接下来,建立一XML的解析器,首先解析结构信息,并初始化具体的XQUERY类;
然后解析数据信息,将数据信息填充到XQUERY之中去.