JSP XML 数据处理

在 JSP 里用 DOM / SAX 解析 XML、用 XPath 取值、输出 XML。

Intermediate
Key Points小文件用 DOM 整棵树读进内存,大文件用 SAX 流式解析;取值用 XPath 最省代码。

示例 XML

XML
<?xml version="1.0" encoding="UTF-8"?>
<books>
  <book id="1">
    <title>深入理解计算机系统</title>
    <price>139.00</price>
  </book>
  <book id="2">
    <title>设计数据密集型应用</title>
    <price>128.00</price>
  </book>
</books>

DOM 解析

JSP
<%@ page contentType="text/html;charset=UTF-8"
         import="javax.xml.parsers.*, org.w3c.dom.*, java.io.*" %>
<%
    DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
    // 关掉外部实体,防 XXE
    f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

    DocumentBuilder b = f.newDocumentBuilder();
    Document doc = b.parse(application.getResourceAsStream("/WEB-INF/books.xml"));

    NodeList books = doc.getElementsByTagName("book");
    for (int i = 0; i < books.getLength(); i++) {
        Element e = (Element) books.item(i);
        String id = e.getAttribute("id");
        String title = e.getElementsByTagName("title").item(0).getTextContent();
        String price = e.getElementsByTagName("price").item(0).getTextContent();
%>
  <p><%= id %> — <%= title %> — ¥<%= price %></p>
<%
    }
%>
Output
1 — 深入理解计算机系统 — ¥139.00
2 — 设计数据密集型应用 — ¥128.00

The disallow-doctype-decl line is mandatory. By default a DOCTYPE can reference external entities, letting an attacker read arbitrary server files (XXE) or probe the internal network.

XPath 取值

JSP
<%@ page import="javax.xml.xpath.*" %>
<%
    XPath xp = XPathFactory.newInstance().newXPath();
    NodeList titles = (NodeList) xp.evaluate(
            "//book[price > 130]/title", doc, XPathConstants.NODESET);
    for (int i = 0; i < titles.getLength(); i++) {
        out.println("<p>" + titles.item(i).getTextContent() + "</p>");
    }

    String first = (String) xp.evaluate("string(//book[1]/title)", doc,
            XPathConstants.STRING);
%>
Output
深入理解计算机系统

XPath collapses traversal and filtering into one expression: "titles of books over 130" is //book[price > 130]/title instead of a loop with comparisons.

SAX 流式解析

Java
public class BookHandler extends DefaultHandler {
    private final List<String> titles = new ArrayList<>();
    private StringBuilder buf;
    private boolean inTitle;

    @Override
    public void startElement(String uri, String local, String qName, Attributes a) {
        if ("title".equals(qName)) { inTitle = true; buf = new StringBuilder(); }
    }

    @Override
    public void characters(char[] ch, int start, int len) {
        if (inTitle) buf.append(ch, start, len);
    }

    @Override
    public void endElement(String uri, String local, String qName) {
        if ("title".equals(qName)) { inTitle = false; titles.add(buf.toString()); }
    }

    public List<String> getTitles() { return titles; }
}
SAX
Memory整棵树进内存只保留当前状态
Speed慢,要建树快,一遍过
Modify可以增删节点只读
Best for配置、小文件日志、大文件

直接输出 XML

JSP
<%@ page contentType="application/xml;charset=UTF-8" %>
<?xml version="1.0" encoding="UTF-8"?>
<users>
  <c:forEach var="u" items="${users}">
    <user id="${u.id}"><c:out value="${u.name}"/></user>
  </c:forEach>
</users>
For XML output the contentType must be application/xml, and no blank line may precede the XML declaration — parsers reject any leading whitespace.

Friends & Partners