更新時間:2021-12-06 08:58:44 來源:動力節點 瀏覽1411次
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天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習