随笔-4  评论-0  文章-0  trackbacks-0
  2006年7月7日

最近的工作常常要和XML格式的文档或字符串打交道,发现用JDOM来做真是方便。可以实现XML应用程序的快速开发。
    在 JDOM 中,XML 元素就是 Element 的实例,XML 属性就是 Attribute 的实例,XML 文档本身就是 Document 的实例。
    因为 JDOM 对象就是像 Document、Element 和 Attribute 这些类的直接实例,因此创建一个新 JDOM 对象就如在 Java 语言中使用 new 操作符一样容易。JDOM 的使用是直截了当的。
    JDOM 使用标准的 Java 编码模式。只要有可能,它使用 Java new 操作符而不故弄玄虚使用复杂的工厂化模式,使对象操作即便对于初学用户也很方便。
   
    本文分两步对JDOM的应用加以介绍:XML创建 和 XML解析
一、XML文档创建
    我们由零开始利用JDOM生成一个XML文档。最后的结果(样本文档)看起来象这样:
    <?xml version="1.0" encoding="UTF-8"?>
    <MyInfo comment="introduce myself">
        <name>kingwong</name>
        <sex value="male"/>
        <contact>
            <telephone>87654321</telephone>
        </contact>
    </MyInfo>
    1.以 MyInfo 为根元素创建文档
        Element rootElement = new Element("MyInfo");//所有的XML元素都是 Element 的实例。根元素也不例外:)
        Document myDocument = new Document(rootElement);//以根元素作为参数创建Document对象。一个Document只有一个根,即root元素。
    2.给根元素添加属性
        Attribute rootAttri = new Attribute("comment","introduce myself");//创建名为 commnet,值为 introduce myself 的属性。
        rootElement.setAttribute(rootAttri);//将刚创建的属性添加到根元素。
        这两行代码你也可以合成一行来写,象这样:
        rootElement.setAttribute(new Attribute("comment","introduce myself"));
        或者
        rootElement.setAttribute("comment","introduce myself");
    3.添加元素和子元素
        JDOM里子元素是作为 content(内容)添加到父元素里面去的,所谓content就是类似上面样本文档中<name></name>之间的东东,即kingwong。罗嗦了点是吧:)
        Element nameElement = new Element("name");//创建 name 元素
        nameElement.addContent("kingwong");//将kingwong作为content添加到name元素
 rootElement.addContent(nameElement);//将name元素作为content添加到根元素
 
 这三行你也可以合为一句,象这样:
 rootElement.addContent((Content)(new Element("name").addContent("kingwong")));//因为addContent(Content child)方法返回的是一个Parent接口,而Element类同时继承了Content类和实现了Parent接口,所以我们把它造型成Content。
 
        我们用同样的方法添加带属性的子元素<sex value="male"/>
        rootElement.addContent(new Element("sex").setAttribute("value","male"));//注意这里不需要转型,因为addAttribute(String name,String value)返回值就是一个 Element。
       
        同样的,我们添加<contract />元素到根元素下,用法上一样,只是稍微复杂了一些:
        rootElement.addContent((Content)(new Element("contact").addContent((Content)(new Element("telephone").addContent("87654321")))));
        如果你对这种简写形式还不太习惯,你完全可以分步来做,就象本节刚开始的时候一样。事实上如果层次比较多,写成分步的形式更清晰些,也不容易出错。
    4.删除子元素
        这个操作比较简单:
        rootElement.removeChild("sex");//该方法返回一个布尔值
       
        到目前为止,我们学习了一下JDOM文档生成操作。上面建立了一个样本文档,可是我们怎么知道对不对呢?因此需要输出来看一下。我们将JDOM生成的文档输出到控制台,使用 JDOM 的 XMLOutputter 类。
    5.  将 JDOM 转化为 XML 文本
        XMLOutputter xmlOut = new XMLOutputter("  ",true);
 try {
  xmlOut.output(myDocument,System.out);
 } catch (IOException e) {
  e.printStackTrace();
 }
 XMLOutputter 有几个格式选项。这里我们已指定希望子元素从父元素缩进两个空格,并且希望元素间有空行。
 new XMLOutputter(java.lang.String indent, boolean newlines)这个方法在最新版本中已经不建议使用。JDOM有一个专门的用来定义格式化输出的类:org.jdom.output.Format,如果你没有特殊的要求,有时候使用里面的几个静态方法(应该可以说是预定义格式)如 getPrettyFormat()就可以了。我们把上面的输出格式稍微改一下,就象这样:
 XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat()); 
    6.将JDOM文档转化为其他形式
        XMLOutputter 还可输出到 Writer 或 OutputStream。为了输出JDOM文档到一个文本文件,我们可以这样做:
        FileWriter writer = new FileWriter("/some/directory/myFile.xml");
        outputter.output(myDocument, writer);
        writer.close();
       
        XMLOutputter 还可输出到字符串,以便程序后面进行再处理:
        Strng outString = xmlOut.outputString(myDocument);
       
        当然,在输出的时候你不一定要输出所有的整个文档,你可以选择元素进行输出:
        xmlOut.output(rootElement.getChild("name"),System.out);
        一句话,JDOM非常灵活方便!如果你想进一步研究JDOM,请到官方网站去看一看:http://www.jdom.org

    本节示例源码:
package com.cyberobject.study;

import java.io.IOException;

import org.jdom.Attribute;
import org.jdom.Content;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/**
 * @author kingwong
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class TestJDOM {

 public static void main(String[] args)
 {
  Element rootElement = new Element("MyInfo");
  Document myDocument = new Document(rootElement);
 
//  Attribute rootAttri = new Attribute("comment","introduce myself");
//  rootElement.setAttribute(rootAttri);
 
  rootElement.setAttribute("comment","introduce myself");
  //rootElement.setAttribute(new Attribute("comment","introduce myself"));
//  Element sexElement = new Element("sex");
//  rootElement.addContent(sexElement);
 
//  Element nameElement = new Element("name");
//  nameElement.addContent("kingwong");
//  rootElement.addContent(nameElement);
 
  rootElement.addContent((Content)(new Element("name").addContent("kingwong")));
  rootElement.addContent(new Element("sex").setAttribute("value","male"));
  rootElement.addContent((Content)(new Element("contract").addContent((Content)(new Element("telephone").addContent("87654321")))));
 
  rootElement.removeChild("sex");
 
  XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat());
  try {
   xmlOut.output(myDocument,System.out);
   //xmlOut.output(rootElement.getChild("name"),System.out);
   //String outString = xmlOut.outputString(myDocument);
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

       
二、XML文档解析
    JDOM 不光可以很方便的建立XML文档,它的另一个用处是它能够读取并操作现有的 XML 数据。
    JDOM的解析器在org.jdom.input.*这个包里,其中的DOMBuilder的功能是将DOM模型的Document解析成JDOM模型的Document;SAXBuilder的功能是从文件或流中解析出符合JDOM模型的XML树。由于我们经常要从一个文件里读取数据,因此我们应该采用后者作为解析工具。
解析一个xml文档,基本可以看成以下几个步骤:
    1.实例化一个合适的解析器对象
        本例中我们使用SAXBuilder:
        SAXBuilder sb = new SAXBuilder();
    2.以包含XML数据的文件为参数,构建一个文档对象myDocument
        Document myDocument = sb.build(/some/directory/myFile.xml);
    3.获到根元素
        Element rootElement = myDocument.getRootElement();
       
        一旦你获取了根元素,你就可以很方便地对它下面的子元素进行操作了,下面对Element对象的一些常用方法作一下简单说明:
        getChild("childname") 返回指定名字的子节点,如果同一级有多个同名子节点,则只返回第一个;如果没有返回null值。
        getChildren("childname") 返回指定名字的子节点List集合。这样你就可以遍历所有的同一级同名子节点。
        getAttributeValue("name") 返回指定属性名字的值。如果没有该属性则返回null,有该属性但是值为空,则返回空字符串。
        getChildText("childname") 返回指定子节点的内容文本值。
        getText() 返回该元素的内容文本值。
       
        还有其他没有罗列出来的方法,如果需要的话,可以随时查阅JDOM的在线文档:http://www.jdom.org/docs/apidocs/index.html。当然你可以在你需要的地方添加、删除元素操作,还记得上面的创建XML的方法吗?呵呵~~~
       
        学习新东东还是从实例学起最为快捷,下面简单举个例子,就以上面的XML样本代码来学习JDOM的XML解析。本例中读取了样本XML文件里一些属性和content,最后我们还在contact元素里插入了一个新元素<email value="wanghua@cyberobject.com" />。尽管我们实现了对于XML的基本操作,细心的朋友可能会
有疑问:如果XML文档的层次稍微复杂一些,如果嵌套多达几十上百层的话(开个玩笑),如果靠这样从根元素一级一级地通过getChild("childname")来访问子元素的话,将会非常痛苦!是的,的确是这样,但是我们有另一个有力的工具XPath,为什么不用呢?这是后话!先卖个关子(手敲累啦,下回吧,呵呵)。
       
/*
 * Created on 2004-8-21
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.cyberobject.study;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/**
 * @author kingwong
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class TestJDOM2 {
 public static void main(String[] args){
 SAXBuilder sb = new SAXBuilder();
    try
    {       
     Document doc = sb.build("myFile.xml");
  Element root = doc.getRootElement();
 
  String str1 = root.getAttributeValue("comment");
  System.out.println("Root Element's comment attribute is : " + str1);
  String str2 = root.getChild("sex").getAttributeValue("value");
  System.out.println("sex Element's value attribute is : " + str2);
  String str3 = root.getChildText("name");
  System.out.println("name Element's content is :" + str3);
  String str4 = root.getChild("contact").getChildText("telephone");
  System.out.println("contact Element's telephone subelement content is : " + str4 + "\n");
  Element inputElement = root.getChild("contact");
  inputElement.addContent(new Element("email").setAttribute("value","wanghua@cyberobject.com"));
 
  XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat());
     String outStr = xmlOut.outputString(root);
     System.out.println(outStr);
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
}
}

posted @ 2006-07-07 11:25 磐石 阅读(307) | 评论 (0)编辑 收藏
  2006年7月2日
使用DOM方式,Java解析XML基本步骤:
首先,我们需要建立一个解析器工厂。
DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
然后可以利用这个工厂来获得一个具体的解析对象。
DocumentBuilder builder=dbf.newDocumentBuilder();
DocumentBuilder的Parse()方法接受一个XML文档名作为输入参数,返回一个Document对象。Document对象代表了 一个XML文档的树模型。
Document doc=builder.parse("candiate.xml");
使用Document对象的getElementsByTagName()方法,我们可以得到一个NodeList对象,他是XML文档中的标签元素 列表,可以使用NodeList对象的item()方法来得列表中的每一个Node对象。
NodeList nl=doc.getElementsByTagName("PERSON");
Element node=(Element)nl.item(i);
最后,我们会使用Node对象的getNodeValue()方法提取某个标签内的内容。
node.getElementsByTagName("NAME").item(0).getFirstChild().getNodeValue()
完整程序代码:
import javax.xml.parsers.*;
import org.w3c.dom.*;

public class dom {
public static void main(String args[]){
String uri=args[0];
try{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//建立一个解析器工厂。
DocumentBuilder builder=factory.newDocumentBuilder();//获得一个具体的解析对象。
Document doc=builder.parse(uri);//返回一个Document对象。
System.out.println(doc.getImplementation());
NodeList nl =doc.getElementsByTagName("PERSON");//得到一个NodeList对象。
for (int i=0;i<nl.getLength();i++){
Element node=(Element) nl.item(i);//得列表中的每一个Node对象。
System.out.print("NAME: ");
System.out.println (node.getElementsByTagName("NAME").item(0).getFirstChild().getNodeValue());
System.out.print("ADDRESS: ");
System.out.println (node.getElementsByTagName("ADDRESS").item(0).getFirstChild().getNodeValue());
System.out.print("TEL: ");
System.out.println (node.getElementsByTagName("TEL").item(0).getFirstChild().getNodeValue());
System.out.print("FAX: ");
System.out.println (node.getElementsByTagName("FAX").item(0).getFirstChild().getNodeValue());
System.out.print("EMAIL: ");
System.out.println (node.getElementsByTagName("EMAIL").item(0).getFirstChild().getNodeValue());
System.out.println();
}
}catch(Exception e){
e.printStackTrace();
}
}
}

posted @ 2006-07-02 10:21 磐石 阅读(3412) | 评论 (0)编辑 收藏
  2006年7月1日
http://blog.chinaitlab.com/user1/270929/subject/1218.html
posted @ 2006-07-01 15:30 磐石 阅读(188) | 评论 (0)编辑 收藏
 
  其实,简单的分析一下,就可以看出客户和服务通讯的主要通道就是Socket本身,而服务器通过accept方法就是同意和客户建立通讯.这样当客户建立Socket的同时。服务器也会使用这一根连线来先后通讯,那么既然如此只要我们存在多条连线就可以了。那么我们的程序可以变为如下:

  服务器:

  import java.io.*;
  import java.net.*;

  public class MyServer {
  public static void main(String[] args) throws IOException{
  ServerSocket server=new ServerSocket(5678);
  while(true){
   Socket client=server.accept();
   BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
   PrintWriter out=new PrintWriter(client.getOutputStream());
   while(true){
    String str=in.readLine();
    System.out.println(str);
    out.println("has receive....");
    out.flush();
    if(str.equals("end"))
     break;
   }
   client.close();
  }
  }
  }

  这里仅仅只是加了一个外层的While循环,这个循环的目的就是当一个客户进来就为它分配一个Socket直到这个客户完成一次和服务器的交互,这里也就是接受到客户的"End"消息.那么现在就实现了多客户之间的交互了。但是.问题又来了,这样做虽然解决了多客户,可是是排队执行的。也就是说当一个客户和服务器完成一次通讯之后下一个客户才可以进来和服务器交互,无法做到同时服务,那么要如何才能同时达到既能相互之间交流又能同时交流呢?很显然这是一个并行执行的问题了。所以线程是最好的解决方案。

  那么下面的问题是如何使用线程.首先要做的事情是创建线程并使得其可以和网络连线取得联系。然后由线程来执行刚才的操作,要创建线程要么直接继承Thread要么实现Runnable接口,要建立和Socket的联系只要传递引用就可以了.而要执行线程就必须重写run方法,而run方法所做的事情就是刚才单线程版本main所做的事情,因此我们的程序变成了这样:

  import java.net.*;
  import java.io.*;

  public class MultiUser extends Thread{
  private Socket client;

  public MultiUser(Socket c){
  this.client=c;
  }

  public void run(){
  try{
   BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
   PrintWriter out=new PrintWriter(client.getOutputStream());
   //Mutil User but can parallel
   while(true){
    String str=in.readLine();
    System.out.println(str);
    out.println("has receive....");
    out.flush();
    if(str.equals("end"))
     break;
   }
   client.close();
  }catch(IOException ex){
  }finally{
  }
  }

  public static void main(String[] args)throws IOException{
  ServerSocket server=new ServerSocket(5678);
  while(true){
  //transfer location change Single User or Multi User
  MultiUser mu=new MultiUser(server.accept());
  mu.start();
  }
  }
  }

  我的类直接从Thread类继承了下来.并且通过构造函数传递引用和客户Socket建立了联系,这样每个线程就有了。一个通讯管道.同样我们可以填写run方法,把之前的操作交给线程来完成,这样多客户并行的Socket就建立起来了。

  以上的代码使用的是

BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out=new PrintWriter(client.getOutputStream());

  还有一种方法是使用

DataInputStream isFromClient = new DataInputStream(client.getInputStream());
DataOutputStream osToClient = new DataOutputStream(client.getOutputStream());

  关于这两种输入输出流的不同,我也只知道前一种对字符串支持比较好,后面对于读取一个字符串需要处理,但是可以支持很多种类型的输出。对于传递字符串而言前一种应该是很好的选择了。
posted @ 2006-07-01 09:38 磐石 阅读(285) | 评论 (0)编辑 收藏
仅列出标题