【加强版】SAX解析XML返回对应格式的Map对象(解决元素递归嵌套)

SAX解析XML返回对应格式的Map对象_辛丑年正月十五的博客-CSDN博客

前言

简介

# 图1.0

阅读必读

# 图1.1

代码呈现

package com.syasuo.xml;import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.*;/*** @author SYASUO* @date 2023/6/19 13:46*/
@Slf4j
public class MyDefaultHandler extends DefaultHandler {//存储结果集private Map<String,Object> resultMap = new HashMap<>();//存储xml元素唯一的标识,主要用于区分元素节点的嵌套private Map<Integer,String> indexKeyId = new HashMap<>();//用下标标记keyprivate Map<Integer,String> indexKeyMap = new HashMap<>();//下标标记的所属key的对象private Map<String,Map<String,Object>> indexKeyMapObj = new HashMap<>();//记录元素节点对应的下标,初始给0private int index = 0;@Overridepublic void startDocument() throws SAXException {log.info("------------------------------XML读取开始------------------------------");}/*** 开始读取XML元素,每次记录下标对应的key和该key对应的对象*/@SneakyThrows@Overridepublic void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {Map<String,Object> node = new HashMap<>();String keyId = null;for (int i = 0; i < attributes.getLength(); i++) {String eleName = attributes.getQName(i);if(null != eleName && eleName.equals("KeyId")){keyId = attributes.getValue(i);}node.put(attributes.getQName(i),attributes.getValue(i));}indexKeyId.put(index,keyId);indexKeyMap.put(index,qName);indexKeyMapObj.put(qName+keyId,node);index++;}/*** 元素节点读取结束时,由于index在startElement中执行了+1操作,* 因此获取对应节点的父级节点时需要先执行index-1操作得到当前节点下标,在当前节点下标位再次执行-1操作得到父级节点* 取到父级节点时需要判断父级包不包含该节点,如果包含就说明该节点是一个List集合,否则就作为一个单独的对象放入父级对象中* 最后给resultMap结果集对象赋值即可*/@SneakyThrows@Overridepublic void endElement(String uri, String localName, String qName) throws SAXException {//找当前节点int j = --index;String keyId = indexKeyId.get(j);//通过组合key找到当前元素对象String curKey = qName+keyId;Map<String,Object> curObj = indexKeyMapObj.get(curKey);if(j > 0){//找父节点j--;String parentQName = indexKeyMap.get(j);String parentKeyId = indexKeyId.get(j);//通过组合key找当前元素对象的父对象String parentKey = parentQName + parentKeyId;Map<String,Object> parent = indexKeyMapObj.get(parentKey);if(parent.containsKey(qName)){List<Map<String,Object>> list = new ArrayList<>();//System.out.println(parent.get(qName).getClass().getTypeName());if(parent.get(qName).getClass().getSimpleName().equals("HashMap")){list.add((Map<String, Object>) parent.get(qName));}else{list.addAll((Collection<? extends Map<String, Object>>) parent.get(qName));}//将当前节点对象加入当前对象集合list.add(curObj);parent.put(qName,list);}else{parent.put(qName,curObj);}resultMap = parent;}}@Overridepublic void characters(char[] ch, int start, int length) throws SAXException {/*String element = new String(ch,start,length);log.info("该方法只有在【<ele>标签值</ele>】这种类型的标签下才能有效获取");*/}@SneakyThrows@Overridepublic void endDocument() throws SAXException {log.info("------------------------------XML读取结束------------------------------");}public Map<String,Object> getResultMap(){return resultMap;}
}

测试运行

注意:CprjInfoDTO是xml对应格式的JavaBean,需要替换成自己的。

XML如何转JavaBean,可以看我这一篇

使用XJC将XML转换成JavaBean遇到的坑_辛丑年正月十五的博客-CSDN博客

package com.syasuo.xml;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.syasuo.dto.CprjInfoDTO;
import com.syasuo.entity.CprjInfo;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.*;
import java.util.Map;/*** @author SYASUO* @date 2023/6/19 13:41*/
public class Test {public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {Long startTime = System.currentTimeMillis();SAXParser parser = SAXParserFactory.newInstance().newSAXParser();MyDefaultHandler myDefaultHandler = new MyDefaultHandler();parser.parse(new File("D://root.xml"), myDefaultHandler);Map<String,Object> resultMap =  myDefaultHandler.getResultMap();OutputStream outputStream = new BufferedOutputStream(new FileOutputStream("D://OutMap.json"));//System.out.println(JSON.toJSONString(resultMap));outputStream.write(JSON.toJSONString(resultMap,true).getBytes());outputStream.close();CprjInfoDTO cprjInfo = JSONObject.parseObject(JSON.toJSONString(resultMap),CprjInfoDTO.class);OutputStream os = new BufferedOutputStream(new FileOutputStream("D://DefaultHandler.json"));os.write(JSON.toJSONString(cprjInfo,true).getBytes());os.close();//System.out.println(JSON.toJSONString(cprjInfo));System.out.println(System.currentTimeMillis()-startTime);}
}

 由于输出太大了,控制台打印不全也不容易看,所以输出了两个文件(详见#图1.2),分别是

1、OutMap.json 存储解析的原生Map对象

2、DefaultHandler.json 存储转换后的java对象

# 图1.2

成功

疑点解惑

问:为什么 j-- 就能找到父级对象?

解:因为sax解析元素是一层一层解析的,当解析嵌套元素时,内部元素没有解析完成时,外部元素是不会进入endElement方法的,所以每次内部元素解析完成通过我记录的下标-1就能找到父级元素 

本文链接:https://my.lmcjl.com/post/13486.html

展开阅读全文

4 评论

留下您的评论.