Sample.java
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.DefaultHandler;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class XmlSample {
// ---- DOM 方式: 読み込み ----
// ツリー全体をメモリに展開するため、小〜中規模向け。
// ランダムアクセスや要素の追加・削除が可能。
public static List<Map<String, String>> parseWithDom(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xml)));
doc.getDocumentElement().normalize();
List<Map<String, String>> employees = new ArrayList<>();
NodeList list = doc.getElementsByTagName("employee");
for (int i = 0; i < list.getLength(); i++) {
Element elem = (Element) list.item(i);
Map<String, String> emp = new LinkedHashMap<>();
emp.put("id", elem.getAttribute("id"));
emp.put("name", getTextContent(elem, "name"));
emp.put("department", getTextContent(elem, "department"));
emp.put("salary", getTextContent(elem, "salary"));
employees.add(emp);
}
return employees;
}
private static String getTextContent(Element parent, String tagName) {
NodeList nodes = parent.getElementsByTagName(tagName);
if (nodes.getLength() > 0) {
return nodes.item(0).getTextContent();
}
return "";
}
// ---- DOM 方式: 書き込み ----
// Transformer を使って DOM ツリーを整形済み XML 文字列に変換する。
public static String buildWithDom(List<Map<String, String>> employees) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
Element root = doc.createElement("employees");
doc.appendChild(root);
for (Map<String, String> emp : employees) {
Element empElem = doc.createElement("employee");
empElem.setAttribute("id", emp.get("id"));
root.appendChild(empElem);
Element nameElem = doc.createElement("name");
nameElem.setTextContent(emp.get("name"));
empElem.appendChild(nameElem);
Element deptElem = doc.createElement("department");
deptElem.setTextContent(emp.get("department"));
empElem.appendChild(deptElem);
Element salaryElem = doc.createElement("salary");
salaryElem.setTextContent(emp.get("salary"));
empElem.appendChild(salaryElem);
}
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
StringWriter sw = new StringWriter();
transformer.transform(new DOMSource(doc), new StreamResult(sw));
return sw.toString();
}
// ---- SAX 方式: 読み込み ----
// イベント駆動でメモリ効率優先。GB 単位の大容量 XML も処理可能。
private static class SaxEmployeeHandler extends DefaultHandler {
final List<Map<String, String>> employees = new ArrayList<>();
private Map<String, String> currentEmp;
private final StringBuilder currentText = new StringBuilder();
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) {
if ("employee".equals(qName)) {
currentEmp = new LinkedHashMap<>();
currentEmp.put("id", attributes.getValue("id"));
}
currentText.setLength(0); // テキストバッファをリセット
}
@Override
public void characters(char[] ch, int start, int length) {
// テキストノードは複数回に分割して呼ばれることがあるため append する
currentText.append(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) {
if (currentEmp == null) {
return;
}
String text = currentText.toString().trim();
if ("name".equals(qName) || "department".equals(qName)
|| "salary".equals(qName)) {
currentEmp.put(qName, text);
}
if ("employee".equals(qName)) {
employees.add(currentEmp);
currentEmp = null;
}
}
}
public static List<Map<String, String>> parseWithSax(String xml) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
SaxEmployeeHandler handler = new SaxEmployeeHandler();
parser.parse(new InputSource(new StringReader(xml)), handler);
return handler.employees;
}
// ---- StAX 方式: 読み込み ----
// カーソル型ストリーム。SAX より直感的で DOM よりメモリ効率が高い。
public static List<Map<String, String>> parseWithStax(String xml) throws Exception {
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(new StringReader(xml));
List<Map<String, String>> employees = new ArrayList<>();
Map<String, String> currentEmp = null;
String currentTag = null;
while (reader.hasNext()) {
int event = reader.next();
if (event == XMLStreamConstants.START_ELEMENT) {
String name = reader.getLocalName();
if ("employee".equals(name)) {
currentEmp = new LinkedHashMap<>();
currentEmp.put("id", reader.getAttributeValue(null, "id"));
}
currentTag = name;
} else if (event == XMLStreamConstants.CHARACTERS && currentEmp != null) {
String text = reader.getText().trim();
if (!text.isEmpty() && currentTag != null) {
if ("name".equals(currentTag) || "department".equals(currentTag)
|| "salary".equals(currentTag)) {
currentEmp.put(currentTag, text);
}
}
} else if (event == XMLStreamConstants.END_ELEMENT) {
if ("employee".equals(reader.getLocalName()) && currentEmp != null) {
employees.add(currentEmp);
currentEmp = null;
}
currentTag = null;
}
}
reader.close();
return employees;
}
}