作者:老九—技术大黍
社交:知乎
公众号:老九学堂(新人有惊喜)
特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权
前言
《Essential XML》的作者Don Box曾开玩笑的说:“XML已经替代了Java、设计模式和对象技术成为当前软件工业的主流”。实际上,XML是非常有用的技术用来描述结构化信息。XML不可以解决一切问题,但是它Java配合得非常好,因为从九十年代末,IBM、Apache和其它的大公司都使用高质量的Java库来处理XML。JDK 1.4以后,Sun公司把很多重要的Java库事例到Java API中去了。
XML与HTML之间区别
虽然XML与HTML都有一个根元素,但是它们有不同如下:
- 不像HTML语言,XML是大小写敏感的,比如H1与h1是不同的XML标签
- 在HTML中你可以省去结束标签,比如"</p>",但是XML不可能省略结束标签
- 在XML中如果元素是单个标签,那么必须使用“/”表示结果,比如"<img src="coffeecup.png" />"
- 在XML中,属性值必须被引号标记。而HTML中,引号是可选的。比如,"<applet code="MyApplet.class" width=300 height=300>是HTML标签,但是不是合法的XML标签。
- 在HTML中属性可以没有值,但是XML的属性必须带值。比如,"<input type="radio" value="java" checked>
XML文档的结构
一个XML文档一般有一个头或者然后跟一个文档定义标签。
<!DOCTYPE web-app PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN” “http://java.sun.com/j2ee/dtds/web-app_2_2.dtd”>
最后一个XML文档体还必须包括根元素,比如:
<configuration>
…
</configuration>
元素和文本是XML文档的“面包和黄油”。下面是我们可能遇见的标记:
1、字符引用的格式如&#小数值;或者&#十六进制;
झ
Ù
2、实体引用的格式如下:
<
>
&
"
'
3、CDATA区<![CDTA[ and ]]>--它们是一种特殊格式的字符数据。 are my favorite delimiters]]>这是< & >不再是关键字了。
4、在XML文档中的处理指令。比如
每个XML文档都一个处理指令:
5、注释标签
解析XML文档
处理一个XML文档,我们需要解析它。一个解析器是一个程序,它用来读取XML文档,读取时需要确定该文档的正确格式,然后分析组成元素,最后让程序员访问这些元素。在Java API中提供了两种XML解析器:
- 树解析器,比如DOM解析器读取XML文档为一个树结构
- 流解析器,比如SAX解析器,它读取XML时产生事件
DOM解析器是标准的W2C组织的标准,它定义的接口为Document和Element两种。不同的提供商需要实现这些接口,比如Apache和IBM都会实现解析器。下面我们来看一下Sun的XML API的使用方式。
Document对象是XML文档在内存中的树结构,它由实现了这些接口的类组成,如上图所示。当我们使用XML API读取XML文档时,我们需要DocumentBuilder对象来实现:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
然后才可以读
File file = … 或者URL u = … 或者InputStream in=…
Document doc = builder.parse(file);
然后才可以分析文档的组成内容
Element root = doc.getDocumentElement();
比如我们解析文档
<?xml version=“1.0”?>
<font>
…
</font>
呼叫getDocumentElement时会返回font元素,呼叫getTagName方法会把一个元素的标签名称;呼叫getChildNodes方法得到子元素,该返回一个集合或者NodeList接口对象,该对象比标准Java集合先出现。item方法根据一个下标得到相应的项,getLength()方法得到所有item的总数。所以,我们可以这样遍历NodeList对象如下:
NodeList children = root.getChildNodes();
for(for i = 0; i < children.getLength(); i++){
Node child = children.item(i);
…
}
如果只想要子元素而忽略空白部分,那么代码如下:
for(int i = 0; i < children.getLength(); i++){
Mode child = children.item(i);
if(child instanceof Element){
Element childElement = (Element)child;
…
}
}
如果你只想处理两个元素”name”和”size”,那么如果有该文档有DTD,那么解析器可以知道哪些元素没有没有文本,而是一个子元素,同时也可以排除空白。下面的代码是使用getData方法得到存贮在Text结点的字符。
for(int i = 0; i < children.getLength(); i++){
Node child = children.item(i);
if(child instanceof Element){
Element childElement = (Element)child;
Text textNode = (Text)childElement.getFirstChild();
String text = textNode.getData().trim();
if(childElement.getTagName().equals(“name”)){
name = text;
}else if(childElement.getTagName().equals(“size”)){
size = Integer.parseInt(text);
}
}
解析XML文档时,我们一般API有javax.xml.parsers包和org.w3c.dom包下面的Document和Element以及Node接口。
ShowDOMTree类
package com.jb.arklis.xml;
import static java.lang.System.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.tree.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
/**
功能:书写一个类用来测试说明DOM树的概念
作者:技术大黍
*/
public class ShowDOMTree{
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
JFrame frame = new DOMTreeFrame();
}
});
}
}
/**
功能:用来演示DOM树的窗体类
*/
class DOMTreeFrame extends JFrame{
private DocumentBuilder builder;
private static final int WIDTH = 650;
private static final int HEIGHT = 450;
public DOMTreeFrame(){
setTitle("演示DOM树的构成");
init();
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
private void init(){
Container container = getContentPane();
//添加菜单
JMenu fileMenu = new JMenu("文件");
JMenuItem openItem = new JMenuItem(" 打开 ");
//添加打开动作处理事件
openItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
openFile(); //呼叫执行打开文件的方法
}
});
fileMenu.add(openItem);
//添加退出菜单
JMenuItem exitItem = new JMenuItem("退出");
exitItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
exit(0); //正确退出系统
}
});
fileMenu.add(exitItem);
JMenuBar menuBar = new JMenuBar();
menuBar.add(fileMenu);
//添加菜单条
setJMenuBar(menuBar);
}
protected void openFile(){
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
//读取文件
chooser.setFileFilter(new javax.swing.filechooser.FileFilter(){
//重写accept()方法选择读取的文件
public boolean accept(File file){
return file.isDirectory() || file.getName().toLowerCase().endsWith(".xml");
}
public String getDescription(){
return "XML文件";
}
});
//从文件选择器返回结果
int returnInt = chooser.showOpenDialog(this);
//如果没有找到xml文件
if(returnInt != JFileChooser.APPROVE_OPTION){
//那么结果程序
return;
}
//否则处理XML文档
final File file = chooser.getSelectedFile();
new SwingWorker<Document, Void>(){
//实现抽象类的方法
protected Document doInBackground()throws Exception{
if(builder == null){
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
builder = factory.newDocumentBuilder();
}
//返回解析的DOM
return builder.parse(file);
}
protected void done(){
try{
Document doc = get();
//使用JTree来显示DOM对象
JTree tree = new JTree(new DOMTreeModel(doc));
tree.setCellRenderer(new DOMTreeCellRender());
setContentPane(new JScrollPane(tree));
validate();
}catch(Exception e){
e.printStackTrace();
JOptionPane.showMessageDialog(DOMTreeFrame.this,e);
}
}
}.execute();
}
}
/**
功能:书写一个用来处理XML文档为树结构的类
*/
class DOMTreeModel implements TreeModel{
private Document doc;
public Object getChild(Object parent, int index){
Node node = (Node)parent;
NodeList list = node.getChildNodes();
return list.item(index);
}
public DOMTreeModel(Document doc){
this.doc = doc;
}
public Object getRoot(){
return doc.getDocumentElement();
}
public int getChildCount(Object parent){
Node node = (Node)parent;
NodeList list = node.getChildNodes();
return list.getLength();
}
public int getIndexOfChild(Object parent, Object child){
Node node = (Node)parent;
NodeList list = node.getChildNodes();
for(int i = 0; i < list.getLength(); i++){
if(getChild(node,i) == child){
return i;
}
}
return -1;
}
public boolean isLeaf(Object node){
return getChildCount(node) == 0;
}
public void valueForPathChanged(TreePath path, Object newValue){
}
public void addTreeModelListener(TreeModelListener listener){
}
public void removeTreeModelListener(TreeModelListener listener){
}
}
/**
功能:书写一个类用来处理XML的结点
*/
class DOMTreeCellRender extends DefaultTreeCellRenderer{
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus){
Node node = (Node)value;
if(node instanceof Element){
return elementPanel((Element)node);
}
super.getTreeCellRendererComponent(tree,value,selected,expanded,leaf,row,hasFocus);
if(node instanceof CharacterData){
setText(characterString((CharacterData)node));
}else{
setText(node.getClass() + ": " + node.toString());
}
return this;
}
public static JPanel elementPanel(Element element){
JPanel panel = new JPanel();
panel.add(new JLabel("元素:" + element.getTagName()));
final NamedNodeMap map = element.getAttributes();
panel.add(new JTable(new AbstractTableModel(){
public int getRowCount(){
return map.getLength();
}
public int getColumnCount(){
return 2;
}
public Object getValueAt(int row, int count){
return count == 0 ? map.item(row).getNodeName() : map.item(row).getNodeValue();
}
}));
return panel;
}
public static String characterString(CharacterData node){
StringBuilder builder = new StringBuilder(node.getData());
for(int i = 0; i < builder.length(); i++){
if(builder.charAt(i) == '\r'){
builder.replace(i, i + 1, "\\r");
i++;
}else if(builder.charAt(i) == '\n'){
builder.replace(i, i + 1, "\\n");
i++;
}else if(builder.charAt(i) == '\t'){
builder.replace(i, i + 1, "\\t");
i++;
}
}
if(node instanceof CDATASection){
builder.insert(0, "CDATASection: ");
}else if(node instanceof Text){
builder.insert(0, "文本: ");
}else if(node instanceof Comment){
builder.insert(0, "注释: ");
}
return builder.toString();
}
}
运行效果
验证XML文档
前面读取XML文档为DOM树,我们看到要处理空白的,以及结点是否是我们所需要的结点内容。为解决这些问题,我们会使用XML解析器的一个功能,就是自动验证XML文档是否为合法的文档。 如果想指明所需要的文档,我们会使用到DTD或者XML Schema来说明客户需求,要求解析器来验证。比如我们使用DTD来指定内容
说明font结点包含的结构,该规则表示了一个font必须有两个子元素,一个叫name另外一个叫size元素。同样我们可以使用Schema来描述:
文档类型定义(DTD)
DTD可以写在XML文档,或者写在XML文档外部,它们的使用格式如下:
<?xml version=“1.0”?>
<!DOCTYPE configuration[
<!ELEMENT configuration …>
more rules
…
]>
<configuration>
…
</configuration>
或者:
ELEMENT规则可以指定元素有子元素,我们可以使用正则表达式,如下:
其中PCDATA表示任意一个字符。
当我们使用DTD时,我们需要告诉factory打开验证功能: factory.setValidating(true); 这样,解析器才会根据DTD来进行XML文档的验证动作。它最常用的功能是忽略元素内容中的空白。比如:
<font>
<name>Helvetica</name>
<size>26</size>
</font>
XML Schema验证
Schema是比DTD更为复杂的验证格式,所以,我们这里只讨论基本的Schema验证。比如指定一个XML文档格式如下:
<?xml version=“1.0”?>
<configuration xmls:sli=http://www.w3.org/2001/XMLSchema-instance xsi:noNapespceSchemaLocation=“config.xsd”>
…
</configuration>
以上文档声明config.xsd为验证文档。
一个schema给每个元素定义一个”type”,该类可以是简单类型—使用一个格式约束的字符串—或者复杂的类型。简单类型如下:
- xsd:string
- xsd:int:
- xsd:boolean
我们使用xsd表示一个XML schema的命名空间,有些有使用xs来表示命名空间。
当我们需要解析器进行schema验证时,我们需要执行以下步骤:
- factory.setNamespaceAware(true);
- final String JAXP_SHEMA_LANGUAGE=java.sun.com/xml/javxp/p…;
- final String W3C_XML_SCHEMA=www.w3.org/2001/XMLSch…;
- factory.setAttribute(JAXP_SHEMA_LANGUAGE, W3C_XML_SCHEMA);
使用XPath装载信息
如果你想指定XML文档中的一段信息,你可以通过导航DOM树结点来实现,而XPath语言可以访问树结点变得非常简单。 假设有XML文档如下:
<configuration>
…
<database>
<username>dbuser</username>
<password>secret</password>
…
</database>
</configuration>
使用DOM方式的步骤如下:
- 得到文档结点
- 枚举出所有的子结点
- 定位到database元素
- 得到第一个子元素,即username元素
- 得到第一个子元素,即一个Text(文本)结点
- 得到它数据
而使用XPath技术,那么我们只需要使用一句话:
/configuration/database/username
一个XPath可以描述XML文档中一组结点,比如 /gridbag/row 或者指定一个特定的元素 /gridbag/row[1]
XPath使用”[ ]”操作符来选择特定的元素;使用”@”操作符来得到属性值(attribute value) /gridbag/row[1]/cell[1]/@anchor 使用函数来计算元素的重复个数 count(/gridbag/row)返回row子元素的数量值。 JDK 1.5以后添加执行XPath表达式的API比如
XPathFactory xpFactory = XPathFactory.newInstance();
XPath path = xpFactory.newXPath();
String userName = path.evaluate(“/configuration/database/username”,doc);
ShowXPath类
package com.jb.arklis.xml;
import static java.lang.System.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.xml.namespace.*;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
/**
功能:书写一个测试XPath API的类
作者:技术大黍
*/
public class ShowXPath{
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
new XPathFrame();
}
});
}
}
/**
功能:显示该演示应用的类
*/
class XPathFrame extends JFrame{
private DocumentBuilder builder;
private Document doc;
private XPath path;
private JTextField expression;
private JTextField result;
private JTextArea docText;
private JComboBox typeCombo;
public XPathFrame(){
setTitle("演示XPath API的使用");
setSize(450,300);
init();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
private void init(){
Container container = getContentPane();
//设置菜单
JMenu fileMenu = new JMenu("文档");
JMenuItem openItem = new JMenuItem("打开");
//给打开项添加事件处理动作
openItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
openFile();
}
});
fileMenu.add(openItem);
JMenuItem exitItem = new JMenuItem("退出");
exitItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
exit(0);
}
});
fileMenu.add(exitItem);
//把菜单添加到菜单工具条中去
JMenuBar menuBar = new JMenuBar();
menuBar.add(fileMenu);
setJMenuBar(menuBar);//给窗体设置菜单工具条
ActionListener listener = new ActionListener(){
public void actionPerformed(ActionEvent event){
evaluate();
}
};
expression = new JTextField(20);
expression.addActionListener(listener);
JButton evaluateButton = new JButton("执行XPath表达式");
evaluateButton.addActionListener(listener);
typeCombo = new JComboBox(new Object[]{
"STRING","NODE","NODESET","NUMBER","BOOLEAN"
});
typeCombo.setSelectedItem("STRING");
JPanel panel = new JPanel();
panel.add(expression);
panel.add(typeCombo);
panel.add(evaluateButton);
docText = new JTextArea(10,40);
result = new JTextField();
result.setBorder(new TitledBorder("XPath查询结果:"));
container.add(panel,BorderLayout.NORTH);
container.add(new JScrollPane(docText),BorderLayout.CENTER);
container.add(result,BorderLayout.SOUTH);
try{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
builder = factory.newDocumentBuilder();
}catch(Exception e){
e.printStackTrace();
JOptionPane.showMessageDialog(this,e);
}
XPathFactory xpFactory = XPathFactory.newInstance();
path = xpFactory.newXPath();
pack();
}
/*处理XML文件的*/
public void openFile(){
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
chooser.setFileFilter(new javax.swing.filechooser.FileFilter(){
public boolean accept(File file){
return file.isDirectory() || file.getName().toLowerCase().endsWith(".xml");
}
public String getDescription(){
return "XML文档";
}
});
int row = chooser.showOpenDialog(this);
if(row != JFileChooser.APPROVE_OPTION){
return;
}
File file = chooser.getSelectedFile();
//下面是关键代码
try{
byte[] bytes = new byte[(int)file.length()];
new FileInputStream(file).read(bytes);
docText.setText(new String(bytes));
doc = builder.parse(file);
}catch(Exception e){
JOptionPane.showMessageDialog(this,e);
}
}
public void evaluate(){
try{
String typeName = (String)typeCombo.getSelectedItem();
//得到命名空间等
QName returnType = (QName)XPathConstants.class.getField(typeName).get(null);
//关键代码
Object evalResult = path.evaluate(expression.getText(),doc,returnType);
//如果是结点集合
if(typeName.equals("NODESET")){
NodeList list = (NodeList)evalResult;
StringBuilder message = new StringBuilder();
message.append("{");
for(int i = 0; i < list.getLength(); i++){
if(i > 0){
message.append(", ");
}
message.append("" + list.item(i));
}
message.append("}");
result.setText(message.toString());
}else{
result.setText("" + evalResult);
}
}catch(Exception e){
JOptionPane.showMessageDialog(null,e);
}
}
}
运行效果
使用命名空间和XML流
Java语言使用包来避免类名重复,XML使用相似的东西叫命名空间(namespace)来避免元素和属性重复。一个命名空间使用URI来进行标识,比如 www.w3.org/2001/XMLSch…
HTTP的URL一般用来定位一个文档,所以我们使用URL来对命名空间进行标识。 JDK 1.6把SAX整合到API中去了, 我们可以使用SAX对XML文档进行流解析的动作。我们使用XML API生成XML文档。
ShowXMLWriter类
package com.jb.arklis.xml;
import static java.lang.System.*;
import java.io.*;
import java.net.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import javax.xml.stream.*;
import java.util.*;
import org.w3c.dom.*;
/**
功能:使用SAX API来解析XML文档
作者:技术大黍
*/
public class ShowXMLWriter{
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
XMLWriterFrame frame = new XMLWriterFrame();
}
});
}
}
/**
功能:书写一个显示XML流输出的例子
作者:Arklis zeng
时间:2009-06-29
地点:北大青鸟锦江中心学术部
版本:ver 1.0.0
备注:
*/
class XMLWriterFrame extends JFrame{
private static final int DEFAULT_WIDTH = 450;
private static final int DEFAULT_HEIGHT = 300;
private RectangleComponent rectangleComponent;
private JFileChooser chooser;
public XMLWriterFrame(){
setTitle("演示XML输出流对象的使用");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
init();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
private void init(){
Container container = getContentPane();
chooser = new JFileChooser();
rectangleComponent = new RectangleComponent();
container.add(rectangleComponent);//添加自定义的组件
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
//下面是设置菜单项
JMenu menu = new JMenu("文件");
menuBar.add(menu);
JMenuItem newItem = new JMenuItem("新建");
menu.add(newItem);
newItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
rectangleComponent.newDrawing();
}
});
JMenuItem saveItem = new JMenuItem("保存为DOM/XSLT");
menu.add(saveItem);
saveItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent evnet){
try{
saveDocument();
}catch(Exception e){
e.printStackTrace();
}
}
});
JMenuItem saveStAxItem = new JMenuItem("保存为标准的XML");
menu.add(saveStAxItem);
saveStAxItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
try{
saveStAX();
}catch(Exception e){
e.printStackTrace();
}
}
});
JMenuItem exitItem = new JMenuItem("退出");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
exit(0);
}
});
}
public void saveDocument()throws TransformerException, IOException{
if(chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION){
return;
}
File file = chooser.getSelectedFile();
Document doc = rectangleComponent.buildDocument();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
"http://www.w3.org/TR/2000/CR-SVG-20000802/DTD/svg-20000802.dtd");
transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,"-//W3C//DTD SVG 20000802//EN");
transformer.setOutputProperty(OutputKeys.INDENT,"yes");
transformer.setOutputProperty(OutputKeys.METHOD,"xml");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.transform(new DOMSource(doc),new StreamResult(new FileOutputStream(file)));
}
public void saveStAX()throws FileNotFoundException,XMLStreamException{
if(chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION){
return;
}
File file = chooser.getSelectedFile();
XMLOutputFactory factory = XMLOutputFactory.newInstance();
XMLStreamWriter writer = factory.createXMLStreamWriter(new FileOutputStream(file));
rectangleComponent.writeDocument(writer);
writer.close();
}
}
/**
功能:处理图形的组件类
*/
class RectangleComponent extends JComponent{
private ArrayList<Rectangle2D> rects;
private ArrayList<Color> colors;
private Random generator;
private DocumentBuilder builder;
public RectangleComponent(){
rects = new java.util.ArrayList<Rectangle2D>();
colors = new java.util.ArrayList<Color>();
generator = new Random();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try{
builder = factory.newDocumentBuilder();
}catch(Exception e){
e.printStackTrace();
}
}
/**
产生画图的方法
*/
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();
}
//下面是画rect
Graphics2D g2 = (Graphics2D)g;
for(int i = 0; i < rects.size(); i++){
g2.setPaint(colors.get(i));
g2.fill(rects.get(i));
}
}
/**
构建文档
*/
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 color = colors.get(i);
Rectangle2D rect = rects.get(i);
Element rectElement = doc.createElement("rect");
rectElement.setAttribute("x","" + rect.getX());
rectElement.setAttribute("y","" + rect.getY());
rectElement.setAttribute("width","" + rect.getWidth());
rectElement.setAttribute("height","" + rect.getHeight());
rectElement.setAttribute("fill","" + colorToString(color));
svgElement.appendChild(rectElement);
}
return doc;
}
/**
把当前的图形保存成svg文档
*/
public void writeDocument(XMLStreamWriter writer){
try{
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 color = colors.get(i);
Rectangle2D rect = rects.get(i);
writer.writeEmptyElement("rect");
writer.writeAttribute("x","" + rect.getX());
writer.writeAttribute("y","" + rect.getY());
writer.writeAttribute("width","" + rect.getWidth());
writer.writeAttribute("height","" + rect.getHeight());
writer.writeAttribute("fill","" + colorToString(color));
}
writer.writeEndDocument();
}catch(Exception e){
e.printStackTrace();
}
}
private static String colorToString(Color color){
StringBuffer buffer = new StringBuffer();
buffer.append(Integer.toHexString(color.getRGB() & 0xFFFFFF));
while(buffer.length() < 6){
buffer.insert(0,'0');
}
buffer.insert(0,'#');
return buffer.toString();
}
}
运行效果
我们把Java2D图形保存为SVG文档。使用XML来处理图形技术参见Apache的Batik小组网站
最后
记得给大黍❤️关注+点赞+收藏+评论+转发❤️
作者:老九学堂—技术大黍
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。