첨부파일이 아니라 내용에 붙였습니다 xmls x = new xmls(...)에서
x.domLoad(...);
....
x.domWrite(...); 이런 방식으로 사용하시면 됩니다
자세한 설명은 생략하겠습니다 xml의 구조에 대해서 조금만 아신다면 금방 파악이 되실 겁니다
상용으로 잘 사용하고 있습니다
붙이다 보니 tab이 먹지 않아서 정렬이 안되어 있는데
copy해서 editor에서 정리한 다음 보십시오 import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
// xercesImpl.jar를 복사한다
// java_home/jre/lib/src.zip에 있음
import org.apache.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.*;
/*****
▧ XML 문서의 구조
- 최상위 요소를 root라고 한다
XML 문서는 단 하나의 root요소만을 가질 수 있다. 이를 흔히 top level이라고 한다.
root요소가 2개 이상일 경우에는 에러가 발생한다
- root요소 앞에는 XML 선언을 담고 있는 프롤로그가 올 수 있다
XML 문서마다 사용된 XML 버전 정보를 프롤로그에 넣어주는것이 좋다
<?xml version="1.0" ?>과 같이 버전 정보가 포함될 수 있다
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="euc-kr"?> encoding정보도 포함될 수 있다
반드시 <?xml... 은 붙여야 한다
- 요소는 반드시 <와 >로 둘러싼 tag로 구성된다
element와 attribute로 나뉘어 진다
<tag>text</tag>
<tag attribute="text">
<tag attribute="text"></tag>
<tag attribute="text"/>
<tag></tag>
<tag/>
- 주석은 <!-- -->이다
▧ DOM (Document Object Model)
- XML문서를 메모리상에 올려놓은 다음 데이타를 찾아 들어가는 방식을 사용한다
- 큰 문서일경우 무조건 다 읽기 때문에 로드 시간이 걸리거나 메모리를 낭비하게 된다
- 문서 편집을 할 수 있다
- 데이타를 저장되어 있는 순서대로 받는다
▧ SAX (Simple API for XML)
- 큰 문서를 효율적으로 분석하기 위해서 사용한다
- 이벤트가 발생하면 그에 해당하는 데이타를 추출한다. 속도가 DOM에 비해서 빠르다
- 읽기 전용이다. 문서를 편집할 수 없다
- 데이타를 SAX가 주는 순서대로 받는다. 파서가 문서를 탐색하는 순서에 대해서 어떠한 작업도 할 수 없다
- 원하는 요소만 골라서 읽을 수 있다
▧ 현재 Java SAX Parser의 종류
- Xerces (Apache Foundation DOM & SAX)
SAX와 DOMahen를 다룰 수 있게하는 XML API를 위하여 Apache Group의 프로젝트이다
org.apache.xerces.parser를 사용한다
- Crimson (Sun Project X, JAXP Default Parser)
Sun ProjectX의 일환으로 JAXP의 default parser로 채택되어져 있으며 XMLReader 인터페이스를 구현하므로 SAX API를 이용하여 여러가지 다양한 파싱기능을 사용할 수 있다
- JAXP (Sun, Java API for XML Processing)
Xerces처럼 DOM과 SAX 둘 다를 사용할 수 있도록 지원해주는 API이다
한가지 다른점은 SAX API의 인터페이스를 직접 구현한 클래스를 제공하는것이 아니라
abstract 계층을 한단계 얹은 형태를 제공한다
- Xalan은 xslt 프로세서이다. xsl을 이용하기 위해 필요하다
*****/
public class Xmls
{
public static final int NONE = 0;
public static final int DOM = 1;
public static final int SAX = 2;
// DOM
private DocumentBuilderFactory dbFactory;
private DocumentBuilder dBuilder;
private Document document = null;
private TransformerFactory tFactory;
private Transformer transformer;
// SAX
private javax.xml.parsers.SAXParserFactory spFactory;
private javax.xml.parsers.SAXParser sParser;
private org.xml.sax.helpers.DefaultHandler defHandler;
// private SAXDbHandler defHandler = null;
// type이 DOM이면 DOM방식으로 아니면 SAX 방식으로 처리한다
public Xmls (int Type, boolean valid, org.xml.sax.helpers.DefaultHandler defHandler) throws Exception
{
if (Type == DOM)
{
dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setValidating(valid); // check를 넘겨주어도 된다
dBuilder = dbFactory.newDocumentBuilder();
// 출력할때 사용한다
tFactory = TransformerFactory.newInstance();
transformer = tFactory.newTransformer();
}
else
{
this.defHandler = defHandler;
spFactory = javax.xml.parsers.SAXParserFactory.newInstance();
sParser = spFactory.newSAXParser();
// xslt 출력
// TransformerFactory factory = TransformerFactory.newInstance();
// Transformer transformer = factory.newTransformer(new SAXSource(new InputSource(stylesheetFile)));
// transformer.transform(new SAXSource(new InputSource(XMLFilename)), new StreamResult(resultFilename));
}
}
//******************** DOM 방식의 method
// xml 문서를 읽는다
// 버전 정보는 반드시 <?xml... 은 붙여야 한다. 아니면 에러가 발생한다
public void domLoad (InputStream in) throws Exception
{
document = dBuilder.parse(in);
}
public void domLoad (String xmlPath) throws Exception
{
document = dBuilder.parse(new File(xmlPath));
}
public Document domGetDocument ()
{
return document;
}
// xml 문자열을 parsing한다
// x.domParse("<node>가</node>");
public void domParse (String xmlString) throws Exception
{
StringReader sr = new StringReader(xmlString);
InputSource is = new InputSource(sr);
document = dBuilder.parse(is);
}
// encoding이 null이거나 ""으면 setting하지 않는다
public void domWrite (OutputStream out, String encoding) throws Exception
{
setEncoding(encoding);
// javax.xml.transform.dom.
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(out);
transformer.transform(source, result);
}
public void domWrite (PrintWriter out, String encoding) throws Exception
{
StringWriter sw = new StringWriter();
setEncoding(encoding);
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(sw);
transformer.transform(source, result);
out.print(sw.toString());
}
// setEncoding("euc-kr");
public void domWrite (String xmlPath, String encoding) throws Exception
{
setEncoding(encoding);
// javax.xml.transform.dom.
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File(xmlPath));
transformer.transform(source, result);
}
// 모든 처리가 끝난 다음 마지막에 부른다
public void domClose ()
{
this.document = null;
}
// element와 attribute를 새로 만들고 싶으면 아래의 순서대로 부른다
// x.domAppendElement("personnel/person", "nation", "korea");
// x.domAppendAttribute("personnel/person/nation", "capital", "서울");
// node마다 각각의 값을 주기 위해서 String[] elementValue를 넘긴다
public void domAppendAttribute (String xpath, String attrName, String[] attrValue) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
Element element;
// append하기 때문에 attribute는 반드시 unique한 값을 가져야 하기 때문에 갯수가 맞지 않으면 에러를 발생시킨다
if (nodes.getLength() != attrValue.length) throw new Exception("node 갯수와 value 갯수가 맞지 않습니다.");
for (int i = 0; i < nodes.getLength(); i++)
{
element = (Element)nodes.item(i);
element.setAttribute(attrName, attrValue[i]);
}
}
// 해당 attribute node를 삭제하고 싶으면 domDeleteElement를 사용한다
public void domDeleteAttribute (String xpath, String attrName) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
Element element;
for (int i = 0; i < nodes.getLength(); i++)
{
element = (Element)nodes.item(i);
element.removeAttribute(attrName);
}
}
// elementValue가 없으면 text node를 append하지 않는다
// x.domAppendElement("personnel/person", "nation", String[] k = { "korea" });
// node마다 각각의 값을 주기 위해서 String[] elementValue를 넘긴다
public void domAppendElement (String xpath, String elementName, String[] elementValue) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
Element element, element2;
Text node;
for (int i = 0; i < nodes.getLength(); i++)
{
// loop안에서 선언한다. 모든 node에 append가 된다
element = document.createElement(elementName);
// nodes의 갯수와 elementValue의 갯수가 달라도 된다
if (i < elementValue.length && Codes.checkNull(elementValue[i]) == false)
{
node = document.createTextNode(elementValue[i]);
element.appendChild(node);
}
element2 = (Element)nodes.item(i);
element2.appendChild(element);
}
}
// x.domDeleteElement("personnel/person")
// 하위 node 전체가 삭제된다
public void domDeleteElement (String xpath) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
Node node;
for (int i = 0; i < nodes.getLength(); i++)
{
node = nodes.item(i);
node.getParentNode().removeChild(node); // 부모 노드에서 자식 노드(자신)를 삭제
}
}
// xml 문자열을 파싱해서 해당 노드에 child로 붙인다
// x.domAppendParse("personnel/person", "<node2><node3></node3></node2>");
// x.domAppendAttribute("personnel/person/node2", "id", new String[] { "1", "2", "3", "4", "5", "6" });
// x.domAppendParse("personnel/person", "<node2><node3 id=\"1\">가</node3></node2>");
public void domAppendParse (String xpath, String xmlString) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
Document tempDoc = makeTempDoc(xmlString);
Node node;
for (int i = 0; i < nodes.getLength(); i++)
{
// loop안에 선언해야 한다.
node = tempDoc.getDocumentElement();
node = document.importNode(node, true);
nodes.item(i).appendChild(node);
}
}
// 기존의 노드에 값만 추가 시킨다
// x.domAppendTextNode("personnel/person/node2/node3", "가");
public void domAppendTextNode (String xpath, String[] txtValue) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
Text node;
for (int i = 0; i < nodes.getLength(); i++)
{
// nodes의 갯수와 txtValue의 갯수가 달라도 된다
if (i < txtValue.length && Codes.checkNull(txtValue[i]) == false)
{
node = document.createTextNode(txtValue[i]);
nodes.item(i).appendChild(node);
}
}
}
// xpath="personnel/person/email"
// attr, element 모두 xpath로 지정하면 갯수를 돌려준다
public int domGetNodeCount (String xpath) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
return nodes.getLength();
}
// xpath="personnel/person", attr="id"
// 해당 값의 index 값을 돌려준다. 중복값이 있으면 처음 발견되는 값의 index를 돌려준다
public int domGetIndexAttribute (String xpath, String attrName, String attrValue) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
NamedNodeMap map;
Node node, node2;
String value = null;
int idx = -1;
for (int i = 0; i < nodes.getLength(); i++)
{
node = nodes.item(i);
map = node.getAttributes();
node2 = map.getNamedItem(attrName);
if (node2 != null && Codes.checkNull(attrValue) == false)
{
value = node2.getNodeValue();
if (attrValue.equals(value)) { idx = i; break; }
}
}
return idx;
}
public ArrayList domGetAttribute (String xpath, String attrName) throws Exception
{
return getAttribute(xpath, attrName, -1);
}
//** attr과 value의 갯수가 다를 수 있기 때문에 원하는 index의 attribute값이 아닐수도 있다
// dom일 경우는 xml의 순서대로 읽어오기 때문에 index로의 처리가 가능하다
public String domGetAttribute (String xpath, String attrName, int index) throws Exception
{
return (String)getAttribute(xpath, attrName, index).get(0);
}
// xpath="personnel/person", attr="id"
public void domSetAttribute (String xpath, String attrName, String attrValue) throws Exception
{
setAttribute(xpath, attrName, null, new String[] { attrValue }, -1, 0);
}
// attribute는 unique하기 때문에 origs, news가 가능하다. index로 변경하지 않아도 된다
// origValue는 원래 값, newValue는 새로운 값. origValue 값이 null이거나 ""이면 전체를 newValue 값으로 setting한다
// 대소문자를 구분하고 싶지 않으면 외부에서 대소문자로 변환해서 넘겨준다
public void domSetAttribute (String xpath, String attrName, String origValue, String newValue) throws Exception
{
setAttribute(xpath, attrName, origValue, new String[] { newValue }, -1, 0);
}
// 해당 index의 attribute를 수정한다
public void domSetAttribute (String xpath, String attrName, String attrValue, int index) throws Exception
{
setAttribute(xpath, attrName, null, new String[] { attrValue }, index, 0);
}
// 기존의 attribute값을 변경한다
public void domSetAttribute (String xpath, String attrName, String[] attrValue) throws Exception
{
setAttribute(xpath, attrName, null, attrValue, -1, attrValue.length);
}
// xpath에 해당하는 값이 여러개이면 여러개 하나면 하나만 돌려준다
// xpath="personnel/person/email"
public ArrayList domGetValue (String xpath) throws Exception
{
return getValue(xpath, -1);
}
//** attr과 value의 갯수가 다를 수 있기 때문에 원하는 index의 attribute값이 아닐수도 있다
// dom일 경우는 xml의 순서대로 읽어오기 때문에 index로의 처리가 가능하다
public String domGetValue (String xpath, int index) throws Exception
{
return (String)getValue(xpath, index).get(0);
}
// domSetAttribute하고 다르다. attribute는 unique하기 때문에 origs, news가 가능하다
// xpath="personnel/person/email"
public void domSetValue (String xpath, String value) throws Exception
{
setValue(xpath, new String[] { value }, -1, 0);
}
//** attr과 value의 갯수가 다를 수 있기 때문에 원하는 index의 attribute값이 아닐수도 있다
// dom일 경우는 xml의 순서대로 읽어오기 때문에 index로의 처리가 가능하다
public void domSetValue (String xpath, String value, int index) throws Exception
{
setValue(xpath, new String[] { value }, index, 0);
}
public void domSetValue (String xpath, String[] value) throws Exception
{
setValue(xpath, value, -1, value.length);
}
// domWrite할때 사용한다
// setEncoding("euc-kr");
private void setEncoding (String encoding)
{
if (Codes.checkNull(encoding)) return;
transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
}
private Document makeTempDoc (String nodeString) throws Exception
{
StringReader sr = new StringReader(nodeString);
InputSource is = new InputSource(sr);
return dBuilder.parse(is);
}
private ArrayList getAttribute (String xpath, String attrName, int index) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
NamedNodeMap map;
Node node, node2;
ArrayList al = new ArrayList();
String value = null;
for (int i = 0; i < nodes.getLength(); i++)
{
if (index >= 0 && i != index) continue;
node = nodes.item(i);
map = node.getAttributes(); // 같은 node를 찾는다
node2 = map.getNamedItem(attrName);
if (node2 != null)
{
value = node2.getNodeValue();
al.add(value);
}
}
return al;
}
private void setAttribute (String xpath, String attrName, String attrValue, String[] setValue, int index, int length) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
NamedNodeMap map;
Node node, node2;
String value = null;
int off;
for (int i = 0; i < nodes.getLength(); i++)
{
if (length > 0 && i >= length) break;
if (index >= 0 && i != index) continue;
node = nodes.item(i);
map = node.getAttributes();
node2 = map.getNamedItem(attrName);
off = (length > 0) ? i : 0;
if (node2 != null)
{
if (index > -1 || Codes.checkNull(attrValue)) node2.setNodeValue(setValue[off]);
else
{
value = node2.getNodeValue();
if (attrValue.equals(value)) { node2.setNodeValue(setValue[off]); break; }
}
}
}
}
private ArrayList getValue (String xpath, int index) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
NodeList children;
Node node;
Node childNode;
ArrayList al = new ArrayList();
// value를 리턴하기 위해서 string 변수를 선언
String value = null;
// 여러개의 element에서 하나의 태그가 빠지면 nodes.getLength()의 갯수가 아예 줄어든다
// al.add(Integer.toString(nodes.getLength()));
for (int i = 0; i < nodes.getLength(); i++)
{
if (index >= 0 && i != index) continue;
node = nodes.item(i);
if (node.hasChildNodes())
{
// child node가 있는 node type은 element와 document뿐이다
if (node.getNodeType() == Node.ELEMENT_NODE)
{
// Element 인 경우에는 자식노드를 검색해서 자식 노드중 텍스트 노드, 그러니까
// 태그 사이의 텍스트 값을 리턴하자 일단.
children = node.getChildNodes();
for (int j = 0; j < children.getLength(); j++)
{
childNode = children.item(j);
if (childNode.getNodeType() == Node.TEXT_NODE)
{
// 자식노드를 순환하다가 TextNode 발견하면 value로 세팅.
// out.println(childNode.getNodeName());
value = childNode.getNodeValue();
al.add(value);
}
}
}
else
{
/*
<personnel>
<person id="Big.Boss">
<name><family>Boss</family> <given>Big</given></name>
<email>chief@foo.com</email>
<link subordinates="one.worker two.worker three.worker four.worker five.worker"/>
</person>
</personnel>
-- person이나 name일 경우 값이 없다.
*/
// Document 인 경우에는 Node value라는 개념이 애매하니까..
// 전체 String 을 리턴하거나 null값을리턴해야 하는데 일단 null을 리턴하기로 하자.
// Do nothing
}
}
else
{
// 자식노드가 없는 Attribute나 Text노드, CDATASection등의 값을 질의한 경우. getNodeValue를 이용.
value = node.getNodeValue();
al.add(value);
}
}
return al;
}
private void setValue (String xpath, String[] value, int index, int length) throws Exception
{
NodeList nodes = XPathAPI.selectNodeList(document, xpath);
NodeList children;
Node node;
int off;
for (int i = 0; i < nodes.getLength(); i++)
{
if (length > 0 && i >= length) break;
if (index >= 0 && i != index) continue;
off = (length > 0) ? i : 0;
node = nodes.item(i);
if (node.hasChildNodes())
{
// 기존 Text노드를 삭제하고 다시 setting해야 한다
// 삭제하지 않으면 기존값에 새로운값이 붙여서 처리된다
children = node.getChildNodes();
for (int j = 0; j < children.getLength(); j++)
{
if (children.item(j) instanceof Text) node.removeChild(children.item(j));
}
if (node.getNodeType() == Node.ELEMENT_NODE) node.appendChild((Text)document.createTextNode(value[off]));
else node.setNodeValue(value[off]);
}
else
{
/*
자식노드가 없는 Attribute나 Text노드, CDATASection등의 값을 질의한 경우.
person, link인 경우에 해당
<personnel>
<person id="Big.Boss">
<name><family>Boss</family> <given>Big</given></name>
<email>chief@foo.com</email>
<link subordinates="one.worker two.worker three.worker four.worker five.worker"/>
</person>
</personnel>
*/
if (node.getNodeType() == Node.ELEMENT_NODE) node.appendChild((Text)document.createTextNode(value[off]));
else node.setNodeValue(value[off]);
}
}
}
//******************** SAX 방식의 method
// defHandler는 saxLoad를 부를때 외부에서 받는다
// overriding을 해야 하기 때문에 외부에서 defHandler를 control을 할 수 있게 한다.
public void saxLoad (InputStream in) throws Exception
{
sParser.parse(in, defHandler);
}
public void saxLoad (String xmlPath) throws Exception
{
sParser.parse(new File(xmlPath), defHandler);
}
public org.xml.sax.helpers.DefaultHandler saxGetDefHandler ()
{
return defHandler;
}
}
출처 : 네이버 카페 - 샤크의 자바프로그램