解析XML生成树状结构 - 极悦
专注Java教育14年 全国咨询/投诉热线:444-1124-454
极悦LOGO图
始于2009,口口相传的Java黄埔军校
首页 hot资讯 解析XML生成树状结构

解析XML生成树状结构

更新时间: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视频,里面的课程内容丰富,通俗易懂,适合没有基础的小伙伴学习,希望对大家能够有所帮助。

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>