JAXP进行DOM和SAX解析
1.常用XML的解析方式:DOM和SAX
1)DOM思想:将整个XML加载内存中,形成文档对象,所以对XML操作都对内存中文档对象进行。
2)SAX思想:一边解析,一边处理,一边释放内存资源---不允许在内存中保留大规模XML数据
3)DOM和SAX的区别
DOM:支持回写,会将整个XML载入内存,以树形结构方式存储
XML比较复杂的时候,或者当你需要随机处理文档中数据的时候不建议使用
SAX:相比DOM是一种更为轻量级的方案
无法在读取过程中修改XML数据
2.常用解析开发包:JAXP、DOM4J
3.DOM解析原理图
4.使用JAXP进行DOM解析
JAXP:(Java API for XML Processing)开发包是JavaSE的一部分,它由以下几个包及其子包组成:
org.w3c.dom:提供DOM方式解析XML的标准接口
org.xml.sax:提供SAX方式解析XML的标准接口
javax.xml:提供了解析XML文档的类
5.JAXP进行DOM解析的实例(增删改查)
book.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?><书架>
<书 ISBN="a11" 出版社="清华大学出版社">
<书名>JavaSE基础</书名>
<作者>张三</作者>
<批发价>35.00元</批发价>
<售价>38.00元</售价>
</书>
<书 ISBN="b11" 出版社="北京大学出版社">
<书名>Android</书名>
<作者>李四</作者>
<售价>38.00元</售价>
</书>
</书架>
JaxpDomDemo.java
package cn.lsl.jaxp;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList; public class JaxpDomDemo {
public static void main(String[] args) throws Exception {
//得到解析工厂DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//得到解析器DocumentBuilder
DocumentBuilder builder = factory.newDocumentBuilder();
//解析指定的XML文档,得到代表内存DOM树的Document对象
Document document = builder.parse("src/book.xml");
test8(document);
}
//1.得到某个具体节点内容:打印第2本书的作者
public static void test1(Document document){
//根据标签的名称获取所有的作者元素
NodeList nodeList = document.getElementsByTagName("作者");
//按照索引取第2个作者元素
Node node = nodeList.item(1);
//打印该元素的文本
String text = node.getTextContent();
System.out.println(text);
} // 2、遍历所有元素节点:打印元素的名称
public static void test2(Node node){
//判断当前节点是不是一个元素节点
if(node.getNodeType() == Node.ELEMENT_NODE){
//如果是:打印他的名称
System.out.println(node.getNodeName());
}
//查找子节点
NodeList nodeList = node.getChildNodes();
int len = nodeList.getLength();
for (int i = 0; i < len; i++) {
Node n = nodeList.item(i);
test2(n);
}
} //3、修改某个元素节点的主体内容:把第一本书的售价改为38.00元
public static void test3(Document document) throws Exception{
//找到第一本书的售价
NodeList nodeList = document.getElementsByTagName("售价");
//设置其主体内容
Node node = nodeList.item(0);
node.setTextContent("38.00元");
//把内存中Document树写回xml文件中
TransformerFactory factory = TransformerFactory.newInstance();
Transformer ts = factory.newTransformer();
ts.transform(new DOMSource(document), new StreamResult("src/book.xml"));
} //4.向指定元素节点中增加子元素节点:第一本中增加子元素<内部价>29.00</内部价>
public static void test4(Document document) throws Exception{
//创建一个新的元素并设置其主体内容
Element e = document.createElement("内部价");
e.setTextContent("29.00元");
//找到第一本书元素
Node firstBookNode = document.getElementsByTagName("书").item(0);
//把新节点挂接到第一本书上
firstBookNode.appendChild(e);
//把内存中Document树写回XML文件中
TransformerFactory factory = TransformerFactory.newInstance();
Transformer ts = factory.newTransformer();
ts.transform(new DOMSource(document), new StreamResult("src/book.xml"));
} //5.向指定元素节点上增加同级元素节点:在第一本书的售价前面增加批发价
public static void test5(Document document) throws Exception{
//创建一个新的元素并设置其中的主题内容
Element e = document.createElement("批发价");
e.setTextContent("35.00元");
//找到第一本书的售价
Node firstPrice = document.getElementsByTagName("售价").item(0);
//在售价的前面加入新建的元素:增加子元素一定要用父元素来增加
firstPrice.getParentNode().insertBefore(e, firstPrice);
//把内存中Document树写回XML文件中
TransformerFactory factory = TransformerFactory.newInstance();
Transformer ts = factory.newTransformer();
ts.transform(new DOMSource(document), new StreamResult("src/book.xml"));
} //6.删除指定元素节点:删除内部价
public static void test6(Document document) throws Exception{
//找到内部价节点,用父节点删除
Node n = document.getElementsByTagName("内部价").item(0);
n.getParentNode().removeChild(n);
//把内存中Document书写回XML文件中
TransformerFactory factory = TransformerFactory.newInstance();
Transformer ts = factory.newTransformer();
ts.transform(new DOMSource(document), new StreamResult("src/book.xml"));
} //7、操作XML文件属性:打印第一本书的出版社
public static void test7(Document document){
//得到第一本书
Node n = document.getElementsByTagName("书").item(0);
//打印指定属性的取值
Element e = (Element)n;
System.out.println(e.getAttribute("出版社"));
} //8、添加一个出版社属性给第二本书
public static void test8(Document document) throws Exception{
//得到第二本书
Node n = document.getElementsByTagName("书").item(1);
//打印指定属性的取值
Element e = (Element)n;
e.setAttribute("出版社", "北京大学出版社");
//把内存中Document树写回XML文件中
TransformerFactory factory = TransformerFactory.newInstance();
Transformer ts = factory.newTransformer();
ts.transform(new DOMSource(document), new StreamResult("src/book.xml"));
}
}
6.案例(学生成绩的增删改查,采用分层开发)
案例原型
exam.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?><exam>
<student examid="444" idcard="333">
<name>李四</name>
<location>大连</location>
<grade>97</grade>
</student>
<student examid="666" idcard="555">
<name>小舒</name>
<location>福建</location>
<grade>90.0</grade>
</student>
</exam>
实体类:Student.java
package cn.lsl.domain; public class Student {
private String idcard;
private String examid;
private String name;
private String location;
private Double grade;
public String getIdcard() {
return idcard;
}
public void setIdcard(String idcard) {
this.idcard = idcard;
}
public String getExamid() {
return examid;
}
public void setExamid(String examid) {
this.examid = examid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public Double getGrade() {
return grade;
}
public void setGrade(Double grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student [examid=" + examid + ", grade=" + grade + ", idcard="
+ idcard + ", location=" + location + ", name=" + name + "]";
}
}
工具类:DocumentUtil.java
package cn.lsl.util; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document;
//操作XML的工具类
//工具类中的异常可以抛也可以处理
public class DocumentUtil {
public static Document getDocument() throws Exception{
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
return builder.parse("src/exam.xml");
} public static void write2xml(Document document) throws Exception{
Transformer ts = TransformerFactory.newInstance().newTransformer();
ts.transform(new DOMSource(document), new StreamResult("src/exam.xml"));
}
}
DAO层:(StudentDao.java)
package cn.lsl.dao;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import cn.lsl.domain.Student;
import cn.lsl.util.DocumentUtil; public class StudentDao { //添加学生信息到XML中
public boolean createStudent(Student s){
boolean result = false;
try{
Document document = DocumentUtil.getDocument();
//创建name、location、grade元素并设置内容
Element nameE = document.createElement("name");
nameE.setTextContent(s.getName());
Element locationE = document.createElement("location");
locationE.setTextContent(s.getLocation());
Element gradeE = document.createElement("grade");
gradeE.setTextContent(s.getGrade()+"");
//创建student元素,并设置属性
Element studentE = document.createElement("student");
studentE.setAttribute("idcard", s.getIdcard());
studentE.setAttribute("examid", s.getExamid()); studentE.appendChild(nameE);
studentE.appendChild(locationE);
studentE.appendChild(gradeE);
//得到exam元素,把student挂接上去
Node node = document.getElementsByTagName("exam").item(0);
node.appendChild(studentE);
//写回XML文件中
DocumentUtil.write2xml(document);
result = true;
}catch(Exception e){
throw new RuntimeException(e); //异常转义。异常链
}
return result;
} //根据准考证号查询学生信息
public Student findStudent(String examid){
Student s = null;
try{
Document document = DocumentUtil.getDocument();
//得到所有student元素
NodeList nodelist = document.getElementsByTagName("student");
//遍历student元素,判断他的examid属性的取值是否与参数匹配
for (int i = 0; i < nodelist.getLength(); i++) {
Node node = nodelist.item(i);
if(node instanceof Element){
Element e = (Element)node;
if(e.getAttribute("examid").equals(examid)){
//如果匹配,说明找到了学生,创建学生对象
s = new Student();
s.setExamid(examid);
s.setIdcard(e.getAttribute("idcard"));
s.setName(e.getElementsByTagName("name").item(0).getTextContent());
s.setLocation(e.getElementsByTagName("location").item(0).getTextContent());
s.setGrade(Double.parseDouble(e.getElementsByTagName("grade").item(0).getTextContent()));
}
}
}
}catch(Exception e){
throw new RuntimeException(e);
}
return s;
} //根据学生姓名删除学生
public boolean deleteStudent(String name){
boolean result = false;
try{
//得到Document对象
Document document = DocumentUtil.getDocument();
NodeList nodelist = document.getElementsByTagName("name");
for(int i=0; i<nodelist.getLength(); i++){
Node node = nodelist.item(i);
if(node.getTextContent().equals(name)){
node.getParentNode().getParentNode().removeChild(node.getParentNode());
//写回XML文档
DocumentUtil.write2xml(document);
result = true;
break;
}
}
}catch(Exception e){
throw new RuntimeException(e);
}
return result;
}
}
View层(Main.java)
package cn.lsl.view; import java.io.BufferedReader;
import java.io.InputStreamReader; import cn.lsl.dao.StudentDao;
import cn.lsl.domain.Student; public class Main {
public static void main(String[] args) {
try{
StudentDao dao = new StudentDao();
System.out.println("a、添加学生\tb、删除学生\tc、查询成绩");
System.out.println("请输入操作类型");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String operation = br.readLine();
if("a".equals(operation)){
//添加操作
System.out.println("请输入学生姓名");
String name = br.readLine();
System.out.println("请输入学生准考证号");
String examid = br.readLine();
System.out.println("请输入学生身份证号");
String idcard = br.readLine();
System.out.println("请输入学生所在地");
String location = br.readLine();
System.out.println("请输入学生成绩");
String grade = br.readLine();
Student s = new Student();
s.setExamid(examid);
s.setIdcard(idcard);
s.setName(name);
s.setLocation(location);
s.setGrade(Double.parseDouble(grade));
//System.out.println(s);
boolean b = dao.createStudent(s);
if(b){
System.out.println("---添加成功---");
}else{
System.out.println("对不起!数据有误");
}
}else if("b".equals(operation)){
System.out.println("请输入要删除的学生姓名:");
String name = br.readLine();
boolean b = dao.deleteStudent(name);
if(b){
System.out.println("--删除成功--");
}else{
System.out.println("对不起!删除失败或者学生不存在");
}
}else if("c".equals(operation)){
//查询操作
System.out.println("请输入要查询的学生准考证号:");
String examid = br.readLine();
Student s = dao.findStudent(examid);
if(s == null){
System.out.println("对不起!您查询的学生不存在");
}else{
System.out.println(s);
}
}else{
System.out.println("请输入正确的操作类型");
}
}catch(Exception e){
System.out.println("对不起!服务器忙!");
}
}
}
7.SAX解析原理图
8. SAX采用事件处理的方式解析XML文件,利用 SAX 解析 XML 文档,涉及两个部分:解析器和事件处理器。
9.SAX解析
实例一:
book.xml
<?xml version="1.0" encoding="UTF-8"?>
<书架>
<书 出版社="清华大学出版社">
<书名>JavaSE基础</书名>
<作者>张三</作者>
<售价>38.00</售价>
<内部价>19.00</内部价>
</书>
<书>
<书名>Android</书名>
<作者>李四</作者>
<售价>28.00</售价>
</书>
</书架>
SAXDemo1.java
package cn.lsl.sax; import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader; //JAXP进行SAX解析
public class SAXDemo1 {
public static void main(String[] args) throws Exception {
//得到解析工厂SAXParserFactory
SAXParserFactory factory = SAXParserFactory.newInstance();
//得到解析器SAXParser
SAXParser parser = factory.newSAXParser();
//得到XML读取器:XMLReader
XMLReader reader = parser.getXMLReader();
//注册内容处理器:ContentHandler
reader.setContentHandler(new MyContentHandler());
//取出XML文档
reader.parse("src/book.xml");
}
} class MyContentHandler implements ContentHandler{ //解析到文档开始时被调用
@Override
public void startDocument() throws SAXException {
System.out.println("解析到了文档的开始");
} //解析到了元素开始时被调用: qName元素名称
@Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
System.out.println("解析到了元素的开始:"+qName);
} //解析到了文本内容被调用
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
System.out.println("文本内容:"+new String(ch,start,length));
} //解析到元素结束时被调用
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
System.out.println("解析到了元素的结束:"+qName);
} //解析到文档结束时被调用
@Override
public void endDocument() throws SAXException {
System.out.println("解析到了文档的结束");
} @Override
public void endPrefixMapping(String prefix) throws SAXException {
// TODO Auto-generated method stub } @Override
public void ignorableWhitespace(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub } @Override
public void processingInstruction(String target, String data)
throws SAXException {
// TODO Auto-generated method stub } @Override
public void setDocumentLocator(Locator locator) {
// TODO Auto-generated method stub } @Override
public void skippedEntity(String name) throws SAXException {
// TODO Auto-generated method stub } @Override
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
// TODO Auto-generated method stub }
}
实例二:
SAXDemo2.java
package cn.lsl.sax; import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader; //打印第2本书的作者
public class SAXDemo2 {
public static void main(String[] args) throws Exception {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(new ContentHandler(){ //匿名内部类
boolean isAuthor = false; //是不是作者标签
int index = 0; //作者标签的索引 @Override
public void startElement(String uri, String localName,
String qName, Attributes atts) throws SAXException {
if("作者".equals(qName)){
isAuthor = true;
}
} @Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if(isAuthor&&index==1){
System.out.println(new String(ch,start,length));
}
} @Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if("作者".equals(qName)){
index++;
}
isAuthor = false;
} @Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub } @Override
public void endPrefixMapping(String prefix) throws SAXException {
// TODO Auto-generated method stub } @Override
public void ignorableWhitespace(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub } @Override
public void processingInstruction(String target, String data)
throws SAXException {
// TODO Auto-generated method stub } @Override
public void setDocumentLocator(Locator locator) {
// TODO Auto-generated method stub } @Override
public void skippedEntity(String name) throws SAXException {
// TODO Auto-generated method stub } @Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub } @Override
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
// TODO Auto-generated method stub } });
reader.parse("src/book.xml");
}
}
实例三:读取XML内容,封装到JavaBean
Book.java
package cn.lsl.domain; public class Book {
private String name;
private String author;
private Double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Book [author=" + author + ", name=" + name + ", price=" + price
+ "]";
}
}
SAXDemo3.java
package cn.lsl.sax; import java.util.ArrayList;
import java.util.List; import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler; import cn.lsl.domain.Book; //读取XML内容,封装到JavaBean
public class SAXDemo3 {
public static void main(String[] args) throws Exception {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
XMLReader reader = parser.getXMLReader();
final List books = new ArrayList();
reader.setContentHandler(new DefaultHandler(){
Book book = null;
String currentTagName = null;
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
if("书".equals(qName)){
book = new Book();
}
currentTagName = qName;
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if("书名".equals(currentTagName)){
book.setName(new String(ch,start,length));
}
if("作者".equals(currentTagName)){
book.setAuthor(new String(ch,start,length));
}
if("售价".equals(currentTagName)){
book.setPrice(Double.parseDouble(new String(ch,start,length)));
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if("书".equals(qName)){
books.add(book);
book = null;
}
currentTagName = null;
}
});
reader.parse("src/book.xml");
for (Object b:books) {
System.out.println(b);
}
}
}
JAXP进行DOM和SAX解析的更多相关文章
- Java SE之XML<二>XML DOM与SAX解析
[文档整理系列] Java SE之XML<二>XML DOM与SAX解析 XML编程:CRUD(Create Read Update Delete) XML解析的两种常见方式: DOM(D ...
- java基础71 XML解析中的【DOM和SAX解析工具】相关知识点(网页知识)
本文知识点(目录):本文下面的“实例及附录”全是DOM解析的相关内容 1.xml解析的含义 2.XML的解析方式 3.xml的解析工具 4.XML的解析原理 5.实例 6 ...
- schema文件及XML文件的DOM和Sax解析
schema文件 <?xml version="1.0" encoding="UTF-8"?> <schema xmlns="htt ...
- XML - 十分钟了解XML结构以及DOM和SAX解析方式
引言 NOKIA 有句著名的广告语:"科技以人为本".不论什么技术都是为了满足人的生产生活须要而产生的.详细到小小的一个手机.里面蕴含的技术也是浩如烟海.是几千年来人类科技的结晶, ...
- iOS开发中XML的DOM和SAX解析方法
一.介绍 dom是w3c指定的一套规范标准,核心是按树形结构处理数据,dom解析器读入xml文件并在内存中建立一个结构一模一样的“树”,这树各节点和xml各标记对应,通过操纵此“树”来处理xml中的文 ...
- Dom,pull,Sax解析XML
本篇随笔将详细讲解如何在Android当中解析服务器端传过来的XML数据,这里将会介绍解析xml数据格式的三种方式,分别是DOM.SAX以及PULL. 一.DOM解析XML 我们首先来看看DOM(Do ...
- Java 中,DOM 和 SAX 解析器有什么不同?
DOM 解析器将整个 XML 文档加载到内存来创建一棵 DOM 模型树,这样可以 更快的查找节点和修改 XML 结构,而 SAX 解析器是一个基于事件的解析器, 不会将整个 XML 文档加载到内存.由 ...
- 使用jaxp对比xml进行SAX解析
package cn.itcast.sax; import java.io.IOException; import javax.xml.parsers.ParserConfigurationExcep ...
- XML解析【介绍、DOM、SAX详细说明、jaxp、dom4j、XPATH】
什么是XML解析 前面XML章节已经说了,XML被设计为"什么都不做",XML只用于组织.存储数据,除此之外的数据生成.读取.传送等等的操作都与XML本身无关! XML解析就是读取 ...
随机推荐
- SQL2005性能分析一些细节功能你是否有用到?(二)
原文:SQL2005性能分析一些细节功能你是否有用到?(二) 上一篇:SQL2005性能分析一些细节功能你是否有用到? 我简单的提到了些关于SQL性能分析最基本的一些方法,下面的文章我会陆续补充.前面 ...
- cscope的使用
转自:http://easwy.com/blog/archives/advanced-vim-skills-cscope/ 本节所用命令的帮助入口: :help cscope 在前面的文章中介绍了利用 ...
- python算法题
python几道简单的算法题 最近看了python的语法,但是总感觉不知道怎么使用它,还是先来敲敲一些简单的程序吧. 1.题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都 ...
- 我所理解的Spring AOP的基本概念
Spring AOP中的概念晦涩难懂,读官方文档更是像读天书,看了非常多样例后,写一些自己理解的一些spring的概念.要理解面向切面编程,要首先理解代理模式和动态代理模式. 如果一个OA系统中的一个 ...
- 2014阿里巴巴研发project师暑期实习生面试经验
2014阿里巴巴研发project师暑期实习生面试经验 作者:林子 Blog: http://blog.csdn.net/u013011841 时间:2014年8月 出处:http://blog.c ...
- BEGINNING SHAREPOINT® 2013 DEVELOPMENT 文件夹
BEGINNING SHAREPOINT® 2013 DEVELOPMENT 文件夹 第一部分--開始使用SharePoint 2013 第1章节--SharePoint 2013 介绍 逐渐了解Sh ...
- ShellExecute函数简单说明
平时在delphi写代码的过程中总是能遇到ShellExecute函数,于是索性将它的使用方法整理一下,由于我在微软的站点上也没能查到个详解(当然我查的中文版,俺菜嘛) ShellExecute函数原 ...
- 硬盘安装Archlinux「2013-12-26」
按照Archlinux的中文WIKI安装完成,最后安装引导失败.原因未知. 折腾的脑袋好大,本来都要放弃了,幸好在贴吧发帖求助,吧友@atmouse耐心热心的帮助 最后重启成功启动.帖子地址:http ...
- Nancy学习
Nancy学习 一.认识Nancy 今天听讲关于Nancy框架的培训,被Nancy的易用性所吸引.故晚上回来梳理了一下知识. 什么是Nancy呢?如标题所述,Nancy是一个轻量级的独立的框架: Na ...
- [译]Java 设计模式 之模板方法
(文章翻译自Java Design Pattern: Template Method) 模板方法设计模式定义了归档特定操作的工作流.它允许子类去修改特定的步奏而不用改变工作流的结构. 下面的例子表示模 ...