xml(三)

friendships

XSL转换

XSL转换(XSLT)机制可是指定将XML文档转换为其他格式的规则,例如,纯文本、XHTML或其他任何XML格式。XSLT通常用来将一个机器可读的XML格式转译为另一种机器可读的XML格式,或者将XML转译为适于人类阅读的标识格式

你需要提供XSLT样式表,它描述了XML文档向某种其他格式转换的规则。XSLT处理器处理这个文档,并产生想要的输出

加入我们想要把下面这个XML文件:

1
2
3
4
5
6
7
8
9
10
11
12
<staff>
<employee>
<name>Carl Cracker</name>
<salary>75000</salary>
<hiredate year="1987" month="12" day="15" />
</employee>
<employee>
<name>Harry Hacker</name>
<salary>50000</salary>
<hiredate year="1989" month="3" day="1" />
</employee>
</staff>

转换成下面的表格:

1
2
3
4
5
6
7
8
<table border="1">
<tr>
<td>Carl Cracker</td><td>$75000</td><td>1987-12-15</td>
</tr>
<tr>
<td>Harry Hacker</td><td>$50000</td><td>1989-3-1</td>
</tr>
</table>

具有转换模板的样式表形式如下:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8">
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html"/>
template1
template2
</xsl:stylesheet>

上例的输出设定为html,还可以设置为xml和text

下面是个典型的模板,match属性的值是一个XPath表达式。该模板表名,每当看到一个XPath集/staff/employee中的一个节点时,做以下动作:

  1. 产生字符串< tr>
  2. 对于要处理的子元素继续应用模板。
  3. 当处理完所有子元素后,产生字符串< /tr>
1
2
3
<xsl:template match="/staff/employee">
<tr><xsl:apply-templates/></tr>
</xsl:template>

XSLT处理器由检查根元素开始其处理过程。每当一个节点匹配某个模板时,就回应应用这个模板(如果匹配多个,就会使用最佳匹配的那个)。如果没有匹配的模板,处理器就执行默认操作。对于文本节点,默认操作是把它的内容包含到输出中去。对于元素,默认操作是不产生输出的,但会继续处理其子节点。

如果想把属性值复制到输出中去,就不得不再做稍微复杂点的操作了。下面是一个例子,当处理hiredate节点时。该模板会产生:

  • 字符串< td>
  • year属性的值
  • 一个连字符
  • month属性的值
  • 一个连字符
  • day属性值
  • 一个连字符
  • 字符串< /td>

xsl:value-of语句用于计算节点集的字符串值。节点集由select属性的XPath值设定。在这个例子中,该路径是相对于当前处理节点的相对路径。节点集通过连接各个节点的字符串值被转换成一个字符串。属性节点的字符串值就是它的值,文本节点的字符串值是它的内容,元素节点的字符串值是它子节点的字符串值的连接(没有属性)

1
2
3
4
5
<xsl:template match="/staff/employee/hiredate">
<td><xsl:value-of select="@year"/>-
<xsl:value-of select="@month"/>-
<xsl:value-of select="@day"/></td>
</xsl:template>

下面这段代码将与上面相同的XML文件转换成text文件。position()函数输出在当前节点在父节点中的索引顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0"?>

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:output method="text"/>

<xsl:template match="/staff/employee">employee.
<xsl:value-of select="position()"/>.name=
<xsl:value-of select="name/text()"/>employee.
<xsl:value-of select="position()"/>.salary=
<xsl:value-of select="salary/text()"/>employee.
<xsl:value-of select="position()"/>.hiredate=
<xsl:value-of select="hiredate/@year"/>-
<xsl:value-of select="hiredate/@month"/>-
<xsl:value-of select="hiredate/@day"/>
</xsl:template>

</xsl:stylesheet>

java中的代码实现

可以使用以下代码框架:

1
2
3
4
5
File styleSheet=new File(fileName);//样式表文件
StreamSource styleSource = new StreamSource(styleSheet);
Transformer t= TransformerFactory.newInstance().
newTransformer(styleSource);
t.transform(source,result);

transform方法的参数是Source和Result接口的实现类的对象。Source接口有3个实现类:

DOMSource SAXSource StreamSource

可以根据一个文件、流、阅读器、URL或来自DOM树节点的DOMSource来构建一个StreamSource。

用于转换器的SAXSource可以根据XML阅读器(实现XMLReader接口的类)构建

1
2
3
4
5
t.transform(new SAXSource(
new EmployeeReader(),new InputSource(
new FileInputStream(fileName))),result);
//fileName(根据XML阅读器(EmployeeReader类)转换)->xml文件
//(根据前面指定的styleSheet样式表)->result

当然大多数的时候都已经有了xml格式的输入数据,只要在一个StreamSource对象上调用transform方法即可

1
t.transform(new StreamSource(file),result);

其转换结果是Result接口实现类的一个对象:

DOMResult SAXResult StreamResult

如果要把结果存储到DOM树中,请使用DocumentBuilder产生一个新的文档节点,并将其包装到DOMResult中:

1
2
Document doc = builder.newDocument();
t.transform(source,new DOMResult(doc));

如果要把结果保存为文件,可以使用StreamResult将输出保存为文件

1
t.transform(source,new StreamResult(file));

示例程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package transformtest;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.AttributesImpl;

public class TransformTest
{
public static void main(String[] args) throws Exception
{
String filename;
if (args.length > 0) filename = args[0];
else filename = "makehtml.xsl";
File styleSheet = new File(filename);
StreamSource styleSource = new StreamSource(styleSheet);

Transformer t = TransformerFactory.newInstance().newTransformer(styleSource);
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty(OutputKeys.METHOD, "xml");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

t.transform(new SAXSource(new EmployeeReader(), new InputSource(new FileInputStream(
"employee.dat"))), new StreamResult(System.out));
}
}

class EmployeeReader implements XMLReader
{
public void parse(InputSource source) throws IOException, SAXException
{
InputStream stream = source.getByteStream();
BufferedReader in = new BufferedReader(new InputStreamReader(stream));
String rootElement = "staff";
AttributesImpl atts = new AttributesImpl();

if (handler == null) throw new SAXException("No content handler");

handler.startDocument();
handler.startElement("", rootElement, rootElement, atts);
String line;
while ((line = in.readLine()) != null)
{
handler.startElement("", "employee", "employee", atts);
StringTokenizer t = new StringTokenizer(line, "|");

handler.startElement("", "name", "name", atts);
String s = t.nextToken();
handler.characters(s.toCharArray(), 0, s.length());
handler.endElement("", "name", "name");

handler.startElement("", "salary", "salary", atts);
s = t.nextToken();
handler.characters(s.toCharArray(), 0, s.length());
handler.endElement("", "salary", "salary");

atts.addAttribute("", "year", "year", "CDATA", t.nextToken());
atts.addAttribute("", "month", "month", "CDATA", t.nextToken());
atts.addAttribute("", "day", "day", "CDATA", t.nextToken());
handler.startElement("", "hiredate", "hiredate", atts);
handler.endElement("", "hiredate", "hiredate");
atts.clear();
handler.endElement("", "employee", "employee");
}

handler.endElement("", rootElement, rootElement);
handler.endDocument();
}

public void setContentHandler(ContentHandler newValue)
{
handler = newValue;
}

public ContentHandler getContentHandler()
{
return handler;
}

// the following methods are just do-nothing implementations
public void parse(String systemId) throws IOException, SAXException
{
}

public void setErrorHandler(ErrorHandler handler)
{
}

public ErrorHandler getErrorHandler()
{
return null;
}

public void setDTDHandler(DTDHandler handler)
{
}

public DTDHandler getDTDHandler()
{
return null;
}

public void setEntityResolver(EntityResolver resolver)
{
}

public EntityResolver getEntityResolver()
{
return null;
}

public void setProperty(String name, Object value)
{
}

public Object getProperty(String name)
{
return null;
}

public void setFeature(String name, boolean value)
{
}

public boolean getFeature(String name)
{
return false;
}

private ContentHandler handler;
}

测试结果如下:

Donate comment here