更新时间:2021-12-06 08:58:44 来源:极悦 浏览1184次
xml文件解析成树状结构
package com.fcar.frameworks.utils;
import com.fcar.frameworks.core.GlobalVar;
import com.fcar.frameworks.utils.io.EncryptedInputStream;
import com.fcar.frameworks.utils.xjson.XJsonToXmlNodeParser;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
public class XmlFile {
private XmlNode mRoot; //需要包装一层用于移植方便
// 因为需要记录文件名,因此这里屏蔽了这个方法,如果有问题,实在需要再开放
/**
* 构造函数,输入为文件输入流
*/
private XmlFile(InputStream is) {
mRoot = new Handler().parse(is);
}
/**
* 构造函数,输入为文件名称
*
* @param fileName 文件名
*/
public XmlFile(String fileName) {
long time = System.currentTimeMillis();
int whichXmlParserToUse = GlobalVar.getWhichXmlParserToUse();
if (whichXmlParserToUse == 0)
mRoot = new Handler().parse(fileName);
else {
File f = new File(fileName);
XJsonToXmlNodeParser parser = null;
InputStream inputStream = null;
try {
// 先尝试从外部数据库zip中读取文件
inputStream = ZipFileManager.instance().getFileInputStream(fileName);
if (inputStream == null) {
if (f.exists())
inputStream = new FileInputStream(f);
else
inputStream = GlobalVar.getContext().getAssets().open(fileName);
}
parser = new XJsonToXmlNodeParser(new EncryptedInputStream(new BufferedInputStream(inputStream))); // 使用BufferedInputStream提升IO速度
mRoot = parser.parse();
} catch (IOException e) {
mRoot = null;
return;
} finally {
if (parser != null)
parser.close();
if (inputStream != null)
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
// inputStream在parser.close中没有关闭
}
}
time = System.currentTimeMillis() - time;
L.i("XmlFile", "It takes " + time + " milliseconds to parse " + fileName);
}
/**
* 获取根节点
*
* @return 该Xml文件的根节点
*/
public XmlNode getRoot() {
return mRoot;
}
}
class Handler extends DefaultHandler {
private final Boolean DBG = true;
private XmlNode root;
private Stack<XmlNode> tmp;
private String tmpStr;
/**
* 解析Xml文件,输入为文件名称
*
* @param fileName 文件名,如果未找到,则尝试从Assets中寻找
* @return Xml根节点
*/
public XmlNode parse(String fileName) {
if (fileName == null)
return null;
root = null;
try {
L.i("XmlFile", "open file --> " + fileName);
File f = new File(fileName);
if (f.exists()) {
return parse(new FileInputStream(f));
}
return parse(GlobalVar.getContext().getAssets().open(fileName));
} catch (FileNotFoundException e) {
L.e("Error", "File not found --> " + fileName);
//e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 处理文件,输入为文件流
*
* @param is 输入数据流
* @return Xml数据流的根节点
*/
public XmlNode parse(InputStream is) {
try {
SAXParser p = SAXParserFactory.newInstance().newSAXParser();
p.parse(is, this);
if (tmp.size() != 0) {
L.e("com.fcar", "error tmp != 0");
return null;
}
tmp = null;
tmpStr = null;
return root;
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
} finally {
if (is != null)
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public void startDocument() throws SAXException {
tmp = new Stack<XmlNode>();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
XmlNode n;
//L.i("com.fcar.start", uri + " " + localName + " " + qName);
if (tmp.size() > 0) {
n = new XmlNode(qName, tmp.peek(), attributes);
n.getParent().addChild(n);
tmp.push(n);
} else {
n = new XmlNode(qName, null, attributes);
root = n;
tmp.push(n);
}
tmpStr = "";
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//有特殊字符,所以更改成这样
tmpStr += new String(ch, start, length).trim();
//tmp.peek().setText(new String(ch, start,length).trim());
//L.i("com.fcar.char", tmp.peek().getText());
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
//L.i("com.fcar.end", uri + " " + localName + " " + qName);
tmp.peek().setText(tmpStr);
tmpStr = "";
//检查语法 可以优化掉
if (DBG) {
XmlNode p = tmp.peek();
if (!p.getName().equals(qName))
L.e("com.fcar.end", p.getName() + " != " + qName);
}
//
tmp.pop();
}
}
package com.fcar.frameworks.utils;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.xml.sax.Attributes;
public class XmlNode implements Serializable {
// private static final long serialVersionUID = 352135146221453293L;
private XmlNode mParent;
private XmlNodeList mChildren;
String mName;
String mText;
HashMap<String, String> mAttrs;
public XmlNode(String name, XmlNode parent, Attributes attrs) {
mParent = parent;
mChildren = new XmlNodeList();
mAttrs = new HashMap<String, String>();
for (int i = 0; i < attrs.getLength(); i++) {
mAttrs.put(attrs.getQName(i), attrs.getValue(i));
}
mName = name;
}
public XmlNode(String name, XmlNode parent) {
mParent = parent;
mChildren = new XmlNodeList();
mAttrs = new HashMap<String, String>();
mName = name;
}
public XmlNode() {
mParent = null;
mChildren = new XmlNodeList();
mAttrs = new HashMap<String, String>();
mName = "NoName";
}
public void addChild(XmlNode child) {
mChildren.add(child);
}
//设置文本
public void setText(String text) {
mText = text;
}
//获取文本
public String getText() {
return mText;
}
//获取节点名称
public String getName() {
return mName;
}
//获取父节点
public XmlNode getParent() {
return mParent;
}
//获取指定名称的第一个子结点
public XmlNode getChild(String name) {
return mChildren.find(name);
}
//获取所有的子节点
public XmlNodeList getChildren() {
return mChildren;
}
public XmlNodeList getChildren(String name) {
XmlNodeList list = new XmlNodeList();
for (int i = 0; i < mChildren.getLength(); i++) {
XmlNode n = mChildren.item(i);
if (name.equals(n.getName())) {
list.add(n);
}
}
return list;
}
public boolean hasAttr(String attrName) {
return mAttrs.containsKey(attrName);
}
public boolean hasChild(String childName) {
return getChild(childName) != null;
}
public boolean hasChildren() {
return mChildren.getList().isEmpty();
}
//获取制定名称的属性
public String getAttr(String attrName) {
return mAttrs.get(attrName);
}
// public Map<String, String> getAttrs() {
// return mAttrs;
// }
//设置属性值
public void setAttr(String attrName, String val) {
mAttrs.put(attrName, val);
}
public void setParent(XmlNode parent) {
mParent = parent;
}
public void setName(String name) {
mName = name;
}
/**
* 获取该节点所在的Xml树的根节点
* @return 该节点所在的Xml树的根节点
*/
public XmlNode getRoot() {
XmlNode root = this;
while (root.getParent() != null)
root = root.getParent();
return root;
}
/**
* (在本节点所在的xml树内)传入一个Xml节点路径,传出该路径所描述的节点。
* @param path 描述一个节点的字符串。该字符串是一串以斜杠(/)分隔开的xml节点名。不需要传入根节点名。如果要使用根节点,则传入空字符串或者"/"。
* 例如:根节点(AUTO)--->子节点1(NODE1)----> 子节点2(NODE2)。其所对应的XmlPath字符串为"/NODE1/NODE2"或者"NODE1/NODE2"
* @return 指定Xml节点
*/
public XmlNode getXmlNodeByPath(String path) {
XmlNode root = getRoot();
String[] paths = path.split("/");
if (paths.length == 0)
return root;
if (paths[0].isEmpty()) {
for (int i = 1; i < paths.length; ++i) {
root = root.getChild(paths[i]);
}
} else {
for (int i = 0; i < paths.length; ++i) {
root = root.getChild(paths[i]);
}
}
return root;
}
public String getXmlPath() {
StringBuilder sb = new StringBuilder();
getXmlPath(sb);
return sb.toString();
}
private void getXmlPath(StringBuilder sb) {
XmlNode parent = getParent();
if (parent != null)
parent.getXmlPath(sb);
sb.append('/').append(getName());
}
}
package com.fcar.frameworks.utils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class XmlNodeList implements Serializable {
// private static final long serialVersionUID = 8516515565121423132L;
private List<XmlNode> mList;
public XmlNodeList()
{
mList = new ArrayList<XmlNode>();
}
public List<XmlNode> getList(){
return mList;
}
public XmlNode find(String name)
{
for(int i =0; i<mList.size(); i++)
{
if(mList.get(i).getName().equals(name))
return item(i);
}
return null;
}
public void add(XmlNode n)
{
mList.add(n);
}
//获取并列节点个数
public int getLength()
{
return mList.size();
}
//获取其中的一个节点
public XmlNode item(int index)
{
return mList.get(index);
}
}
package com.fcar.frameworks.utils.xjson;
import com.fcar.frameworks.core.GlobalVar;
import com.fcar.frameworks.utils.io.DoubleBuffer;
import com.fcar.frameworks.utils.WordBuilder;
import com.fcar.frameworks.utils.XmlNode;
import java.io.*;
import java.util.Stack;
/**
* 将XJson文件解析成{@link XmlNode} 对象的工具类<br/>
* Created by carl on 2016/4/7.
*/
public class XJsonToXmlNodeParser implements Closeable {
private static final String TAG = "XJsonParser";
private static final int EXPECT_TYPE_STRING = 1;
private static final int EXPECT_TYPE_SQUARE_BRACKET_START = 2;
private static final int EXPECT_TYPE_SQUARE_BRACKET_END = 4;
private static final int EXPECT_TYPE_COLON = 8;
private static final int EXPECT_TYPE_CURLY_BRACE_START = 16;
private static final int EXPECT_TYPE_CURLY_BRACE_END = 32;
private static final int EXPECT_TYPE_COMMA = 64;
private static final int EXPECT_TYPE_NAME_STRING = 128;
private static final int EXPECT_TYPE_TEXT_STRING = 256;
private static final int EXPECT_TYPE_COLON_BEFORE_TEXT = 512;
private static final char START_CURLY_BRACE = '{';
private static final char END_CURLY_BRACE = '}';
private static final char START_SQUARE_BRACKET = '[';
private static final char END_SQUARE_BRACKET = ']';
private static final char COLON = ':';
private static final char BACK_SLASH = '\\';
private static final char QUOTATION = '"';
private static final char COMMA = ',';
private DoubleBuffer doubleBuffer;
/**
* 存储当前正在处理的对象的栈
*/
private Stack<XmlNode> stack;
// 以下变量用于类内建输入流,解析完毕时自动关闭输入流
private InputStream inputStream;
public XJsonToXmlNodeParser(InputStream is) throws IOException {
this.stack = new Stack<>();
this.doubleBuffer = new DoubleBuffer(is);
}
/**
* 本方法不能读取被加密的文件
* @param fileName #
* @throws IOException #
*/
public XJsonToXmlNodeParser(String fileName) throws IOException {
File f = new File(fileName);
if (f.exists())
inputStream = new FileInputStream(f);
else
inputStream = GlobalVar.getContext().getAssets().open(fileName);
this.stack = new Stack<>();
this.doubleBuffer = new DoubleBuffer(new BufferedInputStream(inputStream));
}
@Override
public void close() {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
inputStream = null;
}
}
/**
* 解析XJson字符流,解析直到一个对象解析完或者字符流结束。换句话说处理一个花括号对。
* @return 解析出来的对应的XmlNode
* @throws IOException #
*/
public XmlNode parse() throws IOException {
if (this.doubleBuffer == null)
return null;
int ch;
int expectedType = EXPECT_TYPE_CURLY_BRACE_START;
while ((ch = doubleBuffer.read()) != -1) {
if (Character.isWhitespace(ch))
continue;
if (ch == START_CURLY_BRACE) {
if ((expectedType & EXPECT_TYPE_CURLY_BRACE_START) != 0) {
onStartObject();
expectedType = EXPECT_TYPE_NAME_STRING;
} else {
throw new XJsonSyntaxException();
}
} else if (ch == END_CURLY_BRACE) {
if ((expectedType & EXPECT_TYPE_TEXT_STRING) != 0) {
// 没有读取到可选的text值就遇到了反花括号
stack.peek().setText("");
onEndObject();
return stack.pop();
} else if ((expectedType & EXPECT_TYPE_CURLY_BRACE_END) != 0) {
onEndObject();
return stack.pop();
} else {
throw new XJsonSyntaxException();
}
} else if (ch == START_SQUARE_BRACKET) {
if ((expectedType & EXPECT_TYPE_SQUARE_BRACKET_START) != 0) {
onStartArray();
expectedType = EXPECT_TYPE_CURLY_BRACE_END;
} else {
throw new XJsonSyntaxException();
}
} else if (ch == COLON) {
if ((expectedType & EXPECT_TYPE_COLON) != 0) {
onMeetColon();
expectedType = EXPECT_TYPE_STRING;
} else if ((expectedType & EXPECT_TYPE_COLON_BEFORE_TEXT) != 0) {
onMeetColonBeforeText();
expectedType = EXPECT_TYPE_TEXT_STRING;
} else {
throw new XJsonSyntaxException();
}
} else if (ch == COMMA) {
if ((expectedType & EXPECT_TYPE_TEXT_STRING) != 0) {
// 没有读取到可选的text值就遇到了逗号
stack.peek().setText("");
expectedType = EXPECT_TYPE_SQUARE_BRACKET_START | EXPECT_TYPE_STRING;
} else if ((expectedType & EXPECT_TYPE_COMMA) != 0) {
expectedType = EXPECT_TYPE_SQUARE_BRACKET_START | EXPECT_TYPE_STRING;
} else {
throw new XJsonSyntaxException();
}
} else if (ch == QUOTATION) {
// 是一般属性名或者属性值
if ((expectedType & EXPECT_TYPE_STRING) != 0) {
onStartString();
if (isKey)
expectedType = EXPECT_TYPE_COMMA | EXPECT_TYPE_CURLY_BRACE_END; // 属性值已经读取完毕
else
expectedType = EXPECT_TYPE_COLON; // 属性名已经读取完毕
} else if ((expectedType & EXPECT_TYPE_NAME_STRING) != 0) { // 标签名
onStartNameString();
expectedType = EXPECT_TYPE_COLON_BEFORE_TEXT;
} else if ((expectedType & EXPECT_TYPE_TEXT_STRING) != 0) { // 标签内部文本值
onStartTextString();
expectedType = EXPECT_TYPE_COMMA | EXPECT_TYPE_CURLY_BRACE_END;
} else {
throw new XJsonSyntaxException();
}
} else {
// 其它非空白字符
throw new XJsonSyntaxException();
}
}
throw new XJsonStreamEndsUnexpectlyException();
// 理论上在读到数据流尾之前解析出XJson对象
}
/**
* 获取指定字符转意以后的字符,目前并没有什么处理,转义符后面是什么字符就转意什么出来
* @param ch 指定字符
* @return 转意以后的字符
*/
public char getEscapeCharacter(int ch) {
return (char) ch;
}
protected void onStartObject() {
stack.push(new XmlNode());
}
protected void onEndObject() {
}
protected void onStartArray() throws IOException {
XmlNode parent = stack.peek();
do {
XmlNode child = parse();
parent.addChild(child);
child.setParent(parent);
int ch;
while ((ch = doubleBuffer.read()) != -1) {
if (Character.isWhitespace(ch))
continue;
if (ch == COMMA)
break;
if (ch == END_SQUARE_BRACKET) {
return;
}
}
} while (true);
}
@SuppressWarnings("unused")
protected void onEndArray() {
}
protected void onMeetColon() {
}
protected void onMeetColonBeforeText() {
}
private WordBuilder wb = new WordBuilder();
private boolean isKey = true; // true表示当前正在扫描的字符串是属性,否则表示值
private String key = null;
protected void onStartString() throws IOException {
wb.clear();
int ch;
while ((ch = doubleBuffer.read()) != -1) {
if (ch == BACK_SLASH) {
ch = getEscapeCharacter(doubleBuffer.read());
wb.append(ch);
} else if (ch == QUOTATION) {
// 在这里遇到引号("),说明遇到了字符串终结的引号
break;
} else {
wb.append(ch);
}
}
String string = wb.toString();
if (isKey) {
key = string;
isKey = false;
} else {
stack.peek().setAttr(key, string);
isKey = true;
}
}
protected void onStartTextString() throws IOException {
wb.clear();
int ch;
while ((ch = doubleBuffer.read()) != -1) {
if (ch == BACK_SLASH) {
ch = getEscapeCharacter(doubleBuffer.read());
wb.append(ch);
} else if (ch == QUOTATION) {
// 在这里遇到引号(")说明,遇到了字符串终结的引号
break;
} else {
wb.append(ch);
}
}
String string = wb.toString();
stack.peek().setText(string);
}
protected void onStartNameString() throws IOException {
wb.clear();
int ch;
while ((ch = doubleBuffer.read()) != -1) {
if (ch == BACK_SLASH) {
ch = getEscapeCharacter(doubleBuffer.read());
wb.append(ch);
} else if (ch == QUOTATION) {
// 在这里遇到引号(")说明,遇到了字符串终结的引号
break;
} else {
wb.append(ch);
}
}
String string = wb.toString();
stack.peek().setName(string);
}
}
package com.fcar.frameworks.utils.io;
import com.fcar.frameworks.utils.ByteEncryptor;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by carl on 2016/4/12.
*/
public class EncryptedInputStream extends InputStream implements EncryptionInterface {
private InputStream inputStream;
/**
* 正文读到哪个字节了
*/
private int count = 0;
private int version;
public EncryptedInputStream(InputStream is) throws IOException {
inputStream = is;
readVersion();
}
public void readVersion() throws IOException {
byte[] buf = new byte[EncryptionInterface.HEADER_SIZE];
if (inputStream.read(buf) != buf.length) {
throw new EncryptedFileFormatException();
}
if (buf[0] != buf[1])
throw new EncryptedFileFormatException();
version = (buf[0] & 0xff) - 0x54;
switch (version) {
case 1:
// 后面什么都不用管了
break;
default:
throw new EncryptedFileFormatVersionUnsupported();
}
}
@Override
public int read() throws IOException {
switch (version) {
case 1:
return readVersion1();
default:
// 不可能出现了
}
// 不可能走到这里
return -1;
}
public int readVersion1() throws IOException {
int aByte = inputStream.read();
if (aByte == -1)
return -1;
aByte = ByteEncryptor.decrypt(aByte, count++);
return aByte & 0xff;
}
@Override
public int available() throws IOException {
return inputStream.available();
}
}
package com.fcar.frameworks.utils;
/**
* 该类要与发布工具的加密算法一致
* Created by carl on 2016/4/12.
*/
public class ByteEncryptor {
private static final int[] MAGIC_NUMBER = {
0x376fac2, 0x339677BE // 都要用偶数,不改变原数的奇偶性,通过原数据的奇偶性选择Magic Number
};
public static int hashKey(int key) {
// return key * 99839 + 26573 & 0xfffffffe;
return key & 0xfffffffe;
}
public static int encrypt(int toEncrypt, int key) {
key = hashKey(key);
// key的最低位丢弃,避免改变原数的奇偶性,通过原数据的奇偶性选择Magic Number
return (toEncrypt ^ key) + MAGIC_NUMBER[(toEncrypt & 0x7fffffff) % MAGIC_NUMBER.length];
}
public static int decrypt(int toDecrypt, int key) {
key = hashKey(key);
// key的最低位丢弃,避免改变原数的奇偶性,通过原数据的奇偶性选择Magic Number
return toDecrypt - MAGIC_NUMBER[(toDecrypt & 0x7fffffff) % MAGIC_NUMBER.length] ^ key;
}
}
package com.fcar.frameworks.utils;
import com.fcar.frameworks.core.GlobalVar;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* 读取Zip文件内容的辅助类,目前仅用于读取数据库<br/>
* Created by carl on 2016/4/20.
*/
public class ZipFileManager {
private static ZipFileManager instance;
static {
L.e("ZipFile: " + GlobalVar.getCurrDatabase());
instance = new ZipFileManager(new File(GlobalVar.getCurrDatabase()));
}
public static ZipFileManager instance() {
return instance;
}
private ZipFile zipFile;
private ZipFileManager(File zip) {
try {
zipFile = new ZipFile(zip);
} catch (IOException e) {
e.printStackTrace();
zipFile = null;
}
}
public InputStream getFileInputStream(String file) {
if (zipFile == null)
return null;
ZipEntry entry = zipFile.getEntry(file);
if (entry == null)
return null;
try {
return zipFile.getInputStream(entry);
} catch (IOException e) {
return null;
}
}
public void close() throws IOException {
if (zipFile != null)
zipFile.close();
}
}
大家如果对此比较感兴趣,想了解更多相关知识,不妨来关注一下极悦的Java视频,里面的课程内容丰富,通俗易懂,适合没有基础的小伙伴学习,希望对大家能够有所帮助。
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习