xml(二)

刚刚好——薛之谦

StAX解析器

StAX解析器是一种“拉解析器(pull parser)”,与安装事件处理器不同,你只需要使用下面的基本循环来迭代所有事件:

1
2
3
4
5
6
7
InputStream in = url.openStream();
XMLInputFactory factory=XMLInputFactory.newInstance();
XMLStreamReader parser=factory.createXMLStreamReader(in);
wehile(parser.hasNext()){
int event =parser.next();
Call parser methods to obtain event details
}

例如,在解析下面的片段时

1
2
3
4
<font>
<name>Helvetica</name>
<size units="pt">78</size>
</font>

解析器将产生下面的事件:

  1. START_ELEMENT,元素名:font
  2. CHARACTERS,内容:空白字符
  3. START_ELEMENT,元素名:name
  4. CHARACTERS,内容:Helvetica
  5. END_ELEMENT,元素名:name
  6. CHARACTERS,内容:空白字符
  7. START_ELEMENT,元素名:size
  8. CHARACTERS,内容:78
  9. END_ELEMENT,元素名:size
  10. CHARACTERS,内容:空白字符
  11. END_ELEMENT,元素名:font

默认情况下命名空间是使能的,你可以通过修改下面的工厂来使其无效:

1
factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE,false);

示例程序如下:

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
package staxtest;

import java.io.InputStream;
import java.net.URL;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;

public class StAXTest
{
public static void main(String[] args) throws Exception
{
String urlString;
if (args.length == 0)
{
urlString = "https://bigengzhe.github.io/atom.xml";
System.out.println("Using " + urlString);
}
else urlString = args[0];
URL url = new URL(urlString);
InputStream in = url.openStream();//用URL来初始化一个输入流
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader parser = factory.createXMLStreamReader(in);//用输入流来初始化解析器
while (parser.hasNext())
{
int event = parser.next();//返回的常量代表相应的解析事件,
if (event == XMLStreamConstants.START_ELEMENT)
{
if (parser.getLocalName().equals("link"))
{
String href = parser.getAttributeValue(null, "href");
//当前事件是START_ELEMENT时,获取给定属性的值。
//若第一个参数为空,则不检查命名空间
if (href != null)
System.out.println(href);
}
}
}
}
}

结果截图:

生成XML文档

​ 现在你已经知道怎样编写读取XML的java程序了,下面让我们开始介绍他的反向过程。当然你可以选择直接通过一系列print调用,打印出各元素,属性和文本内容来编写XML文件,但其实有更好的办法。

DOM/XSLT方式

可以使用文档的内容构建一棵DOM树,然后写出该树的所有内容。步骤如下:

  1. 通过调用DocumentBuilder类的newDocument方法得到一个空文档

    1
    Document doc = builder.newDocument();
  2. 使用Document类的createElement可以构建文档里的元素

    1
    2
       Element rootElement= doc.createElement(rootName);
    Element childElement=doc.createElement(childName);
  3. 使用createTextNode方法可以构建文本节点

    1
    Text textNode=doc.createTextNode(textcontents);
  4. 使用以下方法可以给文档加上根元素,给父元素加上子节点

    1
    2
    3
    doc.appendChild(rootElement);
    rootElement.appendChild(childElement);
    childElement.appendChild(textNode);
  5. 如果要设置元素属性的话:

    1
    rootElement.setAttribute(name,value);
  6. 使用XSTL将DOM树写入到输出流:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    File f = chooser.getSelectedFile();//得到输出流目标目标文件
    Transformer t = TransformerFactory.newInstance().newTransformer();
    t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
    "http://www.w3.org/TR/2000/CR-SVG-20000802/DTD/svg-20000802.dtd");
    t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//W3C//DTD SVG 20000802//EN");
    t.setOutputProperty(OutputKeys.INDENT, "yes");
    t.setOutputProperty(OutputKeys.METHOD, "xml");
    t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
    t.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(f)));

使用StAX写XML文档

SaAX API使我们可以直接将XML树写出,不用构建DOM树,这需要从某个OutputStream中构建一个XMLstreamWriter。步骤如下

  1. 先得到一个XMLstreamWriter对象

    1
    2
    XMLOutputFactory factory=XMLOutputFactory.newInstance();
    XMLStreamWriter writer=factory.createXMLStreamWriter(out);//out是个输出流对象
  2. 产生XML文件头

    1
    writer.writeStartDocument();
  3. 添加元素

    1
    writer.writeStartElement(name);
  4. 添加属性

    1
    writer.writeAttribute(name,value);
  5. 现在,可以再次通过第三步来添加新的子节点,或者通过下面的语句写出字符

    1
    writer.writeCharacters(text);
  6. 在添加完所有的子节点后,调用下面的方法会使当前元素被关闭

    1
    writer.writeEndElement();
  7. 写出没有子节点的元素(如:< img …/>),可以使用下面的调用

    1
    writer.writeEmptyElement(name);
  8. 最后在文档的结尾调用:

    1
    writer.writeEndDocument();

与使用DOM/XSLT方式一样,我们不必担心属性值和字符数据中的转义字符,但是,我们仍旧有可能会产生非良构的XML,例如具有多个根节点的文档。并且,StAX当前的版本还没有任何对产生缩进输出的支持

示例程序如下:

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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
package xmlwritetest;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
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;

public class XMLWriteTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
XMLWriteFrame frame = new XMLWriteFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}

/**
* A frame with a component for showing a modern drawing.
*/
class XMLWriteFrame extends JFrame
{
public XMLWriteFrame()
{
setTitle("XMLWriteTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

chooser = new JFileChooser();

// add component to frame

comp = new RectangleComponent();
add(comp);

// set up menu bar

JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);

JMenu menu = new JMenu("File");
menuBar.add(menu);

JMenuItem newItem = new JMenuItem("New");
menu.add(newItem);
newItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
comp.newDrawing();
}
});

JMenuItem saveItem = new JMenuItem("Save with DOM/XSLT");
menu.add(saveItem);
saveItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
try
{
saveDocument();
}
catch (Exception e)
{
JOptionPane.showMessageDialog(XMLWriteFrame.this, e.toString());
}
}
});

JMenuItem saveStAXItem = new JMenuItem("Save with StAX");
menu.add(saveStAXItem);
saveStAXItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
try
{
saveStAX();
}
catch (Exception e)
{
JOptionPane.showMessageDialog(XMLWriteFrame.this, e.toString());
}
}
});

JMenuItem exitItem = new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
}

/**
* Saves the drawing in SVG format, using DOM/XSLT
*/
public void saveDocument() throws TransformerException, IOException
{
if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) return;
File f = chooser.getSelectedFile();
Document doc = comp.buildDocument();
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
"http://www.w3.org/TR/2000/CR-SVG-20000802/DTD/svg-20000802.dtd");
t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//W3C//DTD SVG 20000802//EN");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty(OutputKeys.METHOD, "xml");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
t.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(f)));
}

/**
* Saves the drawing in SVG format, using StAX
*/
public void saveStAX() throws FileNotFoundException, XMLStreamException
{
if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) return;
File f = chooser.getSelectedFile();
XMLOutputFactory factory = XMLOutputFactory.newInstance();
XMLStreamWriter writer = factory.createXMLStreamWriter(new FileOutputStream(f));
comp.writeDocument(writer);
writer.close();
}

public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;

private RectangleComponent comp;
private JFileChooser chooser;
}

/**
* A component that shows a set of colored rectangles
*/
class RectangleComponent extends JComponent
{
public RectangleComponent()
{
rects = new ArrayList<Rectangle2D>();
colors = new ArrayList<Color>();
generator = new Random();

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try
{
builder = factory.newDocumentBuilder();
}
catch (ParserConfigurationException e)
{
e.printStackTrace();
}
}

/**
* Create a new random drawing.
*/
public void newDrawing()
{
int n = 10 + generator.nextInt(20);
rects.clear();
colors.clear();
for (int i = 1; i <= n; i++)
{
int x = generator.nextInt(getWidth());
int y = generator.nextInt(getHeight());
int width = generator.nextInt(getWidth() - x);
int height = generator.nextInt(getHeight() - y);
rects.add(new Rectangle(x, y, width, height));
int r = generator.nextInt(256);
int g = generator.nextInt(256);
int b = generator.nextInt(256);
colors.add(new Color(r, g, b));
}
repaint();
}

public void paintComponent(Graphics g)
{
if (rects.size() == 0) newDrawing();
Graphics2D g2 = (Graphics2D) g;

// draw all rectangles
for (int i = 0; i < rects.size(); i++)
{
g2.setPaint(colors.get(i));
g2.fill(rects.get(i));
}
}

/**
* Creates an SVG document of the current drawing.
* @return the DOM tree of the SVG document
*/
public Document buildDocument()
{
Document doc = builder.newDocument();
Element svgElement = doc.createElement("svg");
doc.appendChild(svgElement);
svgElement.setAttribute("width", "" + getWidth());
svgElement.setAttribute("height", "" + getHeight());
for (int i = 0; i < rects.size(); i++)
{
Color c = colors.get(i);
Rectangle2D r = rects.get(i);
Element rectElement = doc.createElement("rect");
rectElement.setAttribute("x", "" + r.getX());
rectElement.setAttribute("y", "" + r.getY());
rectElement.setAttribute("width", "" + r.getWidth());
rectElement.setAttribute("height", "" + r.getHeight());
rectElement.setAttribute("fill", colorToString(c));
svgElement.appendChild(rectElement);
}
return doc;
}

/**
* Writers an SVG document of the current drawing.
* @param writer the document destination
*/
public void writeDocument(XMLStreamWriter writer) throws XMLStreamException
{
writer.writeStartDocument();
writer.writeDTD("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20000802//EN\" "
+ "\"http://www.w3.org/TR/2000/CR-SVG-20000802/DTD/svg-20000802.dtd\">");
writer.writeStartElement("svg");
writer.writeAttribute("width", "" + getWidth());
writer.writeAttribute("height", "" + getHeight());
for (int i = 0; i < rects.size(); i++)
{
Color c = colors.get(i);
Rectangle2D r = rects.get(i);
writer.writeEmptyElement("rect");
writer.writeAttribute("x", "" + r.getX());
writer.writeAttribute("y", "" + r.getY());
writer.writeAttribute("width", "" + r.getWidth());
writer.writeAttribute("height", "" + r.getHeight());
writer.writeAttribute("fill", colorToString(c));
}
writer.writeEndDocument(); // closes svg element
}

/**
* Converts a color to a hex value.
* @param c a color
* @return a string of the form #rrggbb
*/
private static String colorToString(Color c)
{
StringBuffer buffer = new StringBuffer();
buffer.append(Integer.toHexString(c.getRGB() & 0xFFFFFF));
while (buffer.length() < 6)
buffer.insert(0, '0');
buffer.insert(0, '#');
return buffer.toString();
}

private ArrayList<Rectangle2D> rects;
private ArrayList<Color> colors;
private Random generator;
private DocumentBuilder builder;
}
Donate comment here