JAVA 与 MyCat(4) XML解析详解

9 篇文章 0 订阅

通过mycat来学习java了^^。


接前一篇:http://blog.csdn.net/john_chang11/article/details/78715228


前一篇讲到了load方法:

private void load(String dtdFile, String xmlFile) {
		InputStream dtd = null;
		InputStream xml = null;
		try {
			dtd = XMLSchemaLoader.class.getResourceAsStream(dtdFile);
			xml = XMLSchemaLoader.class.getResourceAsStream(xmlFile);
			Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();
			// 先加载所有的DataHost
			loadDataHosts(root);
			// 再加载所有的DataNode
			loadDataNodes(root);
			// 最后加载所有的Schema
			loadSchemas(root);
		} catch (ConfigException e) {
			throw e;
		} catch (Exception e) {
			throw new ConfigException(e);
		} finally {


			if (dtd != null) {
				try {
					dtd.close();
				} catch (IOException e) {
				}
			}


			if (xml != null) {
				try {
					xml.close();
				} catch (IOException e) {
				}
			}
		}
}


第一个知识点:getResourceAsStream(fileName)的文件位置,已经讲了,接着往下看:

Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();

看看ConfigUtil.getDocument(dtd, xml)方法的内容,此时方法的参数值为/rule.dtd和/rule.xml:

public static Document getDocument(final InputStream dtd, InputStream xml) throws ParserConfigurationException,
            SAXException, IOException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        factory.setNamespaceAware(false);
        DocumentBuilder builder = factory.newDocumentBuilder();
        builder.setEntityResolver(new EntityResolver() {
            @Override
            public InputSource resolveEntity(String publicId, String systemId) {
                return new InputSource(dtd);
            }
        });
        builder.setErrorHandler(new ErrorHandler() {
            @Override
            public void warning(SAXParseException e) {
            }

            @Override
            public void error(SAXParseException e) throws SAXException {
                throw e;
            }

            @Override
            public void fatalError(SAXParseException e) throws SAXException {
                throw e;
            }
        });
        return builder.parse(xml);
}
该方法就是通过对rule.xml的解析,返回一个w3c的Document类型,用来存放xml文件内容。这又是一个知识点:


XML文档解析:四种方式的简单总结

对XML的解析网上有许多文章,有兴趣可以搜一下,一大把,这里不做过多重复,只对重点列举一下:

以如下XML文档进行展示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
	<tableRule name="mod-long">
		<rule>
			<columns>user_id</columns>
			<algorithm>mod-long</algorithm>
		</rule>
	</tableRule>
	<tableRule name="mod-long-from">
		<rule>
			<columns>from_user_id</columns>
			<algorithm>mod-long</algorithm>
		</rule>
	</tableRule>
	<tableRule name="mod-long-to">
		<rule>
			<columns>to_user_id</columns>
			<algorithm>mod-long</algorithm>
		</rule>
	</tableRule>

	<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
		<!-- how many data nodes -->
		<property name="count">2</property>
		<property name="defaultNode">0</property>
	</function>

</mycat:rule>



1.DOM解析,适合XML文件小的场景

最原始的解析方式,将整个XML文档读入内存,按层次解析成一棵树,容易理解,可以随机访问树的任意部分,但如果XML文档比较大,会占用大量内存。这种解析方式适用于XML文件比较小的场景。

package io.mycat.test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
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;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;


public class DOMrw {

	public static void main(String[] args) {
		parse();
	}

	public static void parse() {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder builder = dbf.newDocumentBuilder();
			InputStream in = DOMrw.class.getResourceAsStream("/rule.xml");
			Document doc = builder.parse(in);
			// root <mycat:rule>
			Element root = doc.getDocumentElement();
			System.out.print(root.getNodeName());
			NamedNodeMap nnm = root.getAttributes();
			for (int i = 0; i < nnm.getLength(); i++) {
				Node n = nnm.item(i);
				System.out.print(" " + n.getNodeName() + "=" + n.getNodeValue() + " ");
			}
			System.out.println();
			// all tableRule, function node
			NodeList nodes = root.getChildNodes();

			print(nodes, 1);

		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private static void print(NodeList nodes, int ind) {
		NamedNodeMap nnm;
		String txt;
		for (int i = 0; i < nodes.getLength(); i++) {
			Node node = nodes.item(i);
			if (node.getNodeType() == Node.TEXT_NODE) {
				txt = node.getTextContent();
				if (txt.replaceAll("\n", "").replaceAll("\t", "").replaceAll(" ", "").length() > 0) {
					for (int j = 0; j < ind; j++) {
						System.out.print("  ");
					}
					System.out.print("[" + node.getTextContent() + "]");
					System.out.println();
				}
			}
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				for (int j = 0; j < ind; j++) {
					System.out.print("  ");
				}
				System.out.print(node.getNodeName());
				nnm = node.getAttributes();
				for (int j = 0; j < nnm.getLength(); j++) {
					Node n = nnm.item(j);
					System.out.print(" " + n.getNodeName() + "=" + n.getNodeValue() + " ");
				}
				System.out.println();
				NodeList nds = node.getChildNodes();
				if (nds.getLength() == 0) {
					System.out.print(node.getTextContent());
				} else {
					print(nds, ind + 1);
				}
			}
		}
	}

}
执行结果:

mycat:rule xmlns:mycat=http://io.mycat/ 
  tableRule name=mod-long 
    rule
      columns
        [user_id]
      algorithm
        [mod-long]
  tableRule name=mod-long-from 
    rule
      columns
        [from_user_id]
      algorithm
        [mod-long]
  function class=io.mycat.route.function.PartitionByMod  name=mod-long 
    property name=count 
      [2]
    property name=defaultNode 
      [0]

上面的java代码是xml的解析,也就是读,把xml文件里的内容全部打印出来。上面代码里,没有写。

2.SAX解析,内存占用少,响应快,适合XML大文档

SAX与DOM正好相反,占用内存少,但只能顺序访问,不能随机访问。SAX在解析XML文档时,按顺序会遇到各种文档元素,遇到时就触发相应的事件,例如startDocument()文档开始事件,endDocument()文档结束事件,startElement()元素开始事件等等,我们重写这些事件方法,就可以完成我们解析的目的,所以SAX也称为事件驱动解析。

SAX不会将整个XML文档都事先读入内存,读过的部分也不保证缓存在内存里,所以适合XML大文档的读取,但只能顺序读取。由于是边读边触发事件边处理,所以SAX的响应速度很快。

XML文档里的元素种类很多,所以对应的事件也很多,但最常用的是startDocument()、endDocument()、startElement()、endElement()、characters()几个。

package io.mycat.test;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SAXr {
	public static void main(String[] args) {
		parse();
		// write();
	}

	public static void parse() {
		SAXParserFactory saxf = SAXParserFactory.newInstance();

		try {
			SAXParser saxparser = saxf.newSAXParser();
			InputStream in = DOMrw.class.getResourceAsStream("/rule.xml");
			saxparser.parse(in, new MySAXHandler());
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

class MySAXHandler extends DefaultHandler {
	boolean hasAttribute = false;
	Attributes attributes = null;

	public void startDocument() throws SAXException {
		System.out.println("文档开始");
	}

	public void endDocument() throws SAXException {
		System.out.println("文档结束");
	}

	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		if (!uri.isEmpty()) {
			System.out.print("uri=" + uri + " ");
		}
		if (!localName.isEmpty()) {
			System.out.print("localName=" + localName + " ");
		}
		if (!qName.isEmpty()) {
			System.out.print("qName=" + qName + " ");
		}

		for (int i = 0; i < attributes.getLength(); i++) {
			System.out.print(attributes.getQName(i) + ":" + attributes.getValue(i) + " ");
		}
		// System.out.println();
	}

	public void endElement(String uri, String localName, String qName) throws SAXException {

	}

	// characters该当就相当于DOM里的TEXT_NODE一样,处理文本信息
	public void characters(char[] ch, int start, int length) throws SAXException {
		System.out.print(new String(ch, start, length));
	}
}
执行结果:

文档开始
qName=mycat:rule xmlns:mycat:http://io.mycat/ 
	qName=tableRule name:mod-long 
		qName=rule 
			qName=columns user_id
			qName=algorithm mod-long
		
	
	qName=tableRule name:mod-long-from 
		qName=rule 
			qName=columns from_user_id
			qName=algorithm mod-long
		
	
	qName=tableRule name:mod-long-to 
		qName=rule 
			qName=columns to_user_id
			qName=algorithm mod-long
		
	

	qName=function name:mod-long class:io.mycat.route.function.PartitionByMod 
		
		qName=property name:count 2
		qName=property name:defaultNode 0
	

文档结束

SAX在解析时,需要指定handler类,该类继承DefaultHandler,实现各种事件的处理。


3.JDOM解析,使用简单

前面的DOM方式和SAX方式解析XML代码编写比较复杂,为了简化代码可以使用JDOM,而且完成相同的功能JDOM的代码更高效,但是JDOM不能解决所有的问题。JDOM的目的就是为了解决80%最常见的问题,使这些问题的解决得到极大简化,而另外20%不常见问题,则需要自己编码解决,这些问题很少遇到,可以忽略。所以JDOM还是很值得考虑使用的。
JDOM没有自己的解析器,所以使用的是SAX解析器,但在使用上更类似于DOM,更容易理解。

package io.mycat.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

public class JDOMr {
	public static void main(String[] args) {
		parse();
		// write();
	}

	public static void parse() {
		SAXBuilder saxBuilder = new SAXBuilder();

		try {
			InputStream in = JDOMr.class.getResourceAsStream("/rule.xml");
			Document document = saxBuilder.build(in); // 这里的Document是jdom2的Document
			Element root = document.getRootElement(); // 这里的Element是jdom2的Element
			System.out.print("name=" + root.getName() + " ");
			System.out.print("NamespacePrefix=" + root.getNamespacePrefix() + " ");
			System.out.print("NamespaceURI=" + root.getNamespaceURI() + " ");
			List<Attribute> lattr = root.getAttributes();
			for (Attribute attr : lattr) {
				System.out.print(" " + attr.getName() + "=" + attr.getValue() + " ");
			}
			System.out.println();
			// all tableRule, function node
			List<Element> children = root.getChildren();

			print(children, 1);

		} catch (JDOMException | IOException e) {

			e.printStackTrace();
		}
	}

	private static void print(List<Element> children, int ind) {
		for (Element elt : children) {
			for (int j = 0; j < ind; j++) {
				System.out.print("  ");
			}
			System.out.print(elt.getName());
			List<Attribute> lattr = elt.getAttributes();
			for (Attribute attr : lattr) {
				System.out.print(" " + attr.getName() + "=" + attr.getValue() + " ");
			}
			String txt = elt.getTextTrim();
			if (txt.length() > 0) {
				System.out.print("[" + txt + "]");
			}
			System.out.println();
			// all tableRule, function node
			List<Element> ch = elt.getChildren();

			print(ch, ind + 1);
		}
	}
}
执行结果:

name=rule NamespacePrefix=mycat NamespaceURI=http://io.mycat/ 
  tableRule name=mod-long 
    rule
      columns[user_id]
      algorithm[mod-long]
  tableRule name=mod-long-from 
    rule
      columns[from_user_id]
      algorithm[mod-long]
  tableRule name=mod-long-to 
    rule
      columns[to_user_id]
      algorithm[mod-long]
  function name=mod-long  class=io.mycat.route.function.PartitionByMod 
    property name=count [2]
    property name=defaultNode [0]

通过上面代码,可以很明显地看到,输出同样的结束,JDOM的代码要少的多。


4.DOM4J解析、最好的解析方式

DOM4J是JDOM的改进,一开始DOM4J只是JDOM的一个智能分支,后来发展成一款独立的开源软件,DOM4J不但可以解决所有的XML解析问题,还包括集成的 XPath 支持、XML Schema 支持以及用于大文档或流化文档的基于事件的处理。总之,DOM4J性能优异、功能强大、极端易用使用。

越来越多的软件在使用DOM4J,连 Sun 的 JAXM 也在用 DOM4J。所以使用DOM4J处理XML是不错的选择。

package io.mycat.test.xml;

import java.io.InputStream;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class DOM4Jr {
	public static void main(String[] args) {
		parse();
		// write();
	}

	public static void parse() {
		SAXReader reader = new SAXReader();
		try {
			InputStream in = JDOMr.class.getResourceAsStream("/rule.xml");
			Document document = reader.read(in); // 这里的Document是dom4j的Document
			Element root = document.getRootElement(); // 这里的Element是dom4j的Element
			System.out.print("name=" + root.getName() + " ");
			System.out.print("NamespacePrefix=" + root.getNamespacePrefix() + " ");
			System.out.print("NamespaceURI=" + root.getNamespaceURI() + " ");
			List<Attribute> lattr = root.attributes();
			for (Attribute attr : lattr) {
				System.out.print(" " + attr.getName() + "=" + attr.getValue() + " ");
			}
			System.out.println();
			// all tableRule, function node
			List<Element> children = root.elements();

			print(children, 1);

		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	private static void print(List<Element> children, int ind) {
		for (Element elt : children) {
			for (int j = 0; j < ind; j++) {
				System.out.print("  ");
			}
			System.out.print(elt.getName());
			List<Attribute> lattr = elt.attributes();
			for (Attribute attr : lattr) {
				System.out.print(" " + attr.getName() + "=" + attr.getValue() + " ");
			}
			String txt = elt.getTextTrim();
			if (txt.length() > 0) {
				System.out.print("[" + txt + "]");
			}
			System.out.println();
			// all tableRule, function node
			List<Element> ch = elt.elements();

			print(ch, ind + 1);
		}
	}
}
执行结果:

name=rule NamespacePrefix=mycat NamespaceURI=http://io.mycat/ 
  tableRule name=mod-long 
    rule
      columns[user_id]
      algorithm[mod-long]
  tableRule name=mod-long-from 
    rule
      columns[from_user_id]
      algorithm[mod-long]
  tableRule name=mod-long-to 
    rule
      columns[to_user_id]
      algorithm[mod-long]
  function name=mod-long  class=io.mycat.route.function.PartitionByMod 
    property name=count [2]
    property name=defaultNode [0]

从上面代码可以看出,DOM4J与JDOM的代码非常类似,代码依然简练,但DOM4J提供的方法更多,完成的功能更多,效率也更高。


上面就是JAVA的XML的四种解析方式,总的来说,优先使用DOM4J的方式。



现在回过头来再看一下MyCat的XML解析:

   public static Document getDocument(final InputStream dtd, InputStream xml) throws ParserConfigurationException,
            SAXException, IOException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        factory.setNamespaceAware(false);
        DocumentBuilder builder = factory.newDocumentBuilder();
        builder.setEntityResolver(new EntityResolver() {
            @Override
            public InputSource resolveEntity(String publicId, String systemId) {
                return new InputSource(dtd);
            }
        });
        builder.setErrorHandler(new ErrorHandler() {
            @Override
            public void warning(SAXParseException e) {
            }

            @Override
            public void error(SAXParseException e) throws SAXException {
                throw e;
            }

            @Override
            public void fatalError(SAXParseException e) throws SAXException {
                throw e;
            }
        });
        return builder.parse(xml);
    }
首先:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        factory.setNamespaceAware(false);
        DocumentBuilder builder = factory.newDocumentBuilder();
这一段用的是javax.xml.parsers包里的JAVA自带的基本类。接下来:

builder.setEntityResolver(new EntityResolver() {
            @Override
            public InputSource resolveEntity(String publicId, String systemId) {
                return new InputSource(dtd);
            }
        });
        builder.setErrorHandler(new ErrorHandler() {
            @Override
            public void warning(SAXParseException e) {
            }

            @Override
            public void error(SAXParseException e) throws SAXException {
                throw e;
            }

            @Override
            public void fatalError(SAXParseException e) throws SAXException {
                throw e;
            }
        });
这一段用的又是SAX的XML解析,最后:

return builder.parse(xml);
返回的又是org.w3c.dom包的Document类。
总的感觉,又点不伦不类,当然不是说这样不能用,就是觉得风格不统一。其实,看SAX部分的作用,就是为了用DTD文件进行XML的校验,而这种校验用JDOM或DOM4J也可以完成。所以可以改成JDOM或DOM4J的统一风格,本人将这段代码改成DOM4J的方式:

	public static Document getDocument(final InputStream dtd, InputStream xml) throws DocumentException {
		SAXReader reader = new SAXReader();
		reader.setValidation(true);
		reader.setEntityResolver(new EntityResolver() {
			public InputSource resolveEntity(String publicId, String systemId) {
				return new InputSource(dtd);
			}
		});

		return reader.read(xml);
	}
这以看到,代码相当简洁,而完成的功能又完全一样。这里的Document是DOM4J里的Document类,不再引用W3C的Document类,风格相当统一。本人已将MyCat里XML解析全部修改成DOM4J的方式,代码简洁不少。

上面内容,如有问题,欢迎指正。












  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值