使用Java SAX解析XML数组元素:通用代码实现求助
解决SAX解析XML数组的通用方案
我来帮你搞定这个SAX解析XML数组的通用问题!SAX本身是事件驱动的解析器,没有内置的“数组识别”能力,但我们可以通过跟踪解析上下文、自定义数组规则来实现通用的重复子元素(也就是你说的XML数组)收集逻辑,把它们存储到List中。
核心逻辑
SAX解析是按顺序触发事件的,我们需要:
- 用栈结构维护当前解析的元素层级,方便跟踪父元素和当前元素的关系
- 定义规则:当父元素下出现多个同名子元素时,将这些子元素实例收集到List中
- 动态管理对象:遇到子元素时创建对应实例,判断是否属于数组,再决定是作为单个属性还是添加到List
代码实现(Java示例)
1. 定义实体类
先对应你的XML结构创建实体类:
class Bookstore { private List<Book> books = new ArrayList<>(); // Getter and Setter public List<Book> getBooks() { return books; } public void setBooks(List<Book> books) { this.books = books; } } class Book { private String category; private String title; private String author; private int year; private double price; // Getters and Setters public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
2. 通用SAX处理器实现
这个处理器支持预定义数组规则,也可以扩展为动态识别:
import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; public class GenericArraySAXHandler extends DefaultHandler { // 维护元素层级的栈,存储当前解析的对象 private Stack<Object> elementStack = new Stack<>(); // 存储当前元素的文本内容 private StringBuilder currentText = new StringBuilder(); // 预定义数组规则:key=父元素名,value=子元素名(表示该父元素下的子元素是数组) private Map<String, String> arrayRules = new HashMap<>(); public GenericArraySAXHandler() { // 配置你的XML数组规则:bookstore下的book是数组 arrayRules.put("bookstore", "book"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { currentText.setLength(0); // 根据标签名创建对应对象(这里可以用反射进一步通用化) Object currentObj = createObjectByTagName(qName); // 设置元素属性 setAttributes(currentObj, attributes); // 处理数组逻辑:如果当前元素是父元素下的数组子元素,添加到List if (!elementStack.isEmpty()) { Object parentObj = elementStack.peek(); String parentTagName = getTagNameFromObject(parentObj); if (arrayRules.containsKey(parentTagName) && arrayRules.get(parentTagName).equals(qName)) { List<Object> arrayList = getOrCreateArrayList(parentObj, qName); arrayList.add(currentObj); } else { // 非数组子元素,直接设置为父对象的属性 setProperty(parentObj, qName, currentObj); } } elementStack.push(currentObj); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { Object currentObj = elementStack.pop(); // 处理叶子节点的文本内容 if (currentText.length() > 0) { setProperty(currentObj, qName, currentText.toString().trim()); } currentText.setLength(0); } @Override public void characters(char[] ch, int start, int length) throws SAXException { currentText.append(ch, start, length); } // 辅助方法:根据标签名创建对象(可扩展为反射实现) private Object createObjectByTagName(String tagName) { switch (tagName) { case "bookstore": return new Bookstore(); case "book": return new Book(); default: return new HashMap<>(); // 通用情况用Map存储属性 } } // 辅助方法:获取对象对应的XML标签名 private String getTagNameFromObject(Object obj) { if (obj instanceof Bookstore) return "bookstore"; if (obj instanceof Book) return "book"; if (obj instanceof Map) return (String) ((Map<?, ?>) obj).get("tagName"); return ""; } // 辅助方法:设置对象的XML属性 private void setAttributes(Object obj, Attributes attributes) { if (obj instanceof Book) { Book book = (Book) obj; book.setCategory(attributes.getValue("category")); } // 通用场景可通过反射遍历属性设置 } // 辅助方法:设置对象的属性(文本或子对象) private void setProperty(Object obj, String propertyName, Object value) { if (obj instanceof Book) { Book book = (Book) obj; switch (propertyName) { case "title": book.setTitle((String) value); break; case "author": book.setAuthor((String) value); break; case "year": book.setYear(Integer.parseInt((String) value)); break; case "price": book.setPrice(Double.parseDouble((String) value)); break; } } else if (obj instanceof Map) { ((Map<String, Object>) obj).put(propertyName, value); } // 通用场景可通过反射调用setter方法 } // 辅助方法:获取或创建父对象中的List private List<Object> getOrCreateArrayList(Object parentObj, String listName) { if (parentObj instanceof Bookstore) { return ((Bookstore) parentObj).getBooks(); } else if (parentObj instanceof Map) { Map<String, Object> parentMap = (Map<String, Object>) parentObj; @SuppressWarnings("unchecked") List<Object> list = (List<Object>) parentMap.get(listName + "List"); if (list == null) { list = new ArrayList<>(); parentMap.put(listName + "List", list); } return list; } return new ArrayList<>(); } // 获取解析后的根对象 public Object getRootObject() { return elementStack.isEmpty() ? null : elementStack.get(0); } }
3. 测试解析代码
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.File; public class SAXArrayParserDemo { public static void main(String[] args) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); GenericArraySAXHandler handler = new GenericArraySAXHandler(); // 替换为你的XML文件路径或输入流 saxParser.parse(new File("books.xml"), handler); Bookstore bookstore = (Bookstore) handler.getRootObject(); // 输出解析结果 for (Book book : bookstore.getBooks()) { System.out.println("书名: " + book.getTitle()); System.out.println("作者: " + book.getAuthor()); System.out.println("价格: " + book.getPrice()); System.out.println("---"); } } catch (Exception e) { e.printStackTrace(); } } }
通用化改进方向
如果要适配更多不同结构的XML,可以做以下优化:
- 反射替代硬编码:用反射根据标签名映射实体类,自动调用setter方法,不需要修改处理器代码就能适配新的XML
- 注解标记数组:在实体类的List字段上添加自定义注解(比如
@XmlArrayElement(name="book")),处理器通过读取注解自动识别数组 - 动态识别数组:不需要预定义规则,当父元素下第二次遇到同名子元素时,自动将原来的单个属性转为List,再添加新实例
内容的提问来源于stack exchange,提问作者Tarun
相关产品推荐
相关产品推荐

