반응형
업무상 이유로 HTML 엔티티가 데이터로 포함되어 있는 XML 문서를 다룰 일이 있었다. XML에는 기본적으로 5개의 엔티티가 정의되어 있다. 그 외의 데이터는 DTD를 통해 선언을 해주거나 엔티티로 해석할 수 없도록 이스케이핑(Escaping)을 해줘야 한다.
HTML 엔티티가 포함되어 있는 XML 문서를 파싱해서 DOM 트리를 사용해야 하는 경우 설정이 좀 까다로울 수가 있다. 이런저런 설정을 찾다가 StAXParser를 이용해서 DOM 트리를 만들어주는 코드를 작성해봤다. & 문자로 시작하는 엔티티를 해석하지 않고, 문자 그대로 사용하는 동작을 구현해봤다. (물론 실무에는 사용하지 않고 그냥 연습 코드로만 작성해봤다. 현실에서는 XML 문서에다가 이상한 엔티티 섞지말아달라고 생산자에게 안내를 해줘야한다.)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.ByteArrayInputStream; | |
import java.util.Iterator; | |
import javax.xml.parsers.DocumentBuilder; | |
import javax.xml.parsers.DocumentBuilderFactory; | |
import javax.xml.parsers.ParserConfigurationException; | |
import javax.xml.stream.XMLEventReader; | |
import javax.xml.stream.XMLInputFactory; | |
import javax.xml.stream.XMLStreamException; | |
import javax.xml.stream.events.Attribute; | |
import javax.xml.stream.events.Characters; | |
import javax.xml.stream.events.EntityReference; | |
import javax.xml.stream.events.ProcessingInstruction; | |
import javax.xml.stream.events.StartElement; | |
import javax.xml.stream.events.XMLEvent; | |
import org.w3c.dom.Document; | |
import org.w3c.dom.Element; | |
import org.w3c.dom.Node; | |
import org.w3c.dom.Text; | |
/** | |
* Parse XML Document using StAXParser and Build DOM tree | |
*/ | |
public class DOMBuilderUsingStAXParser { | |
private Document doc; | |
public DOMBuilderUsingStAXParser() throws ParserConfigurationException { | |
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | |
factory.setNamespaceAware(true); | |
DocumentBuilder builder = factory.newDocumentBuilder(); | |
doc = builder.newDocument(); | |
} | |
/** | |
* StAX Parser를 이용하여 Dom Tree를 만들어 내는 파서 메소드 | |
*/ | |
public Document parse(String xml) throws XMLStreamException { | |
XMLEventReader eventReader = getXmlEventReader(xml); | |
// Cursor | |
Node currentNode = doc; | |
StringBuilder sb = null; | |
while (eventReader.hasNext()) { | |
XMLEvent nextEvent = eventReader.nextEvent(); | |
if (nextEvent.isEntityReference()) { | |
EntityReference event = (EntityReference)nextEvent; | |
// Entity Reference 를 찾지 못해서 튀어 나온 경우 Entity 이름을 그대로 써준다. | |
if (sb == null) | |
sb = new StringBuilder(); | |
sb.append('&').append(event.getName()).append(';'); | |
} else if (nextEvent.isStartElement()) { | |
StartElement event = nextEvent.asStartElement(); | |
if (sb != null) { | |
Text text = doc.createTextNode(sb.toString()); | |
sb = null; | |
currentNode.appendChild(text); | |
} | |
// TODO NameSpace 고려하도록 코딩 | |
Element element = doc.createElement(event.getName().toString()); | |
Iterator<Attribute> attrs = event.getAttributes(); | |
while(attrs.hasNext()) { | |
Attribute attrEvent = attrs.next(); | |
element.setAttribute(attrEvent.getName().toString(), attrEvent.getValue()); | |
} | |
currentNode.appendChild(element); | |
currentNode = element; | |
} else if (nextEvent.isEndElement()) { | |
if (sb != null) { | |
Text text = doc.createTextNode(sb.toString()); | |
sb = null; | |
currentNode.appendChild(text); | |
} | |
currentNode = currentNode.getParentNode(); | |
} else if (nextEvent.isCharacters()) { | |
Characters event = nextEvent.asCharacters(); | |
if (sb == null) | |
sb = new StringBuilder(); | |
sb.append(event.getData()); | |
} else if (nextEvent.isProcessingInstruction()) { | |
ProcessingInstruction event = (ProcessingInstruction)nextEvent; | |
org.w3c.dom.ProcessingInstruction node = doc.createProcessingInstruction(event.getTarget(), event.getData()); | |
currentNode.appendChild(node); | |
} | |
} | |
return doc; | |
} | |
private XMLEventReader getXmlEventReader(String xml) throws XMLStreamException { | |
XMLInputFactory inputFactory = XMLInputFactory.newInstance(); | |
inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); | |
return inputFactory.createXMLEventReader(new ByteArrayInputStream(xml.getBytes())); | |
} | |
} |
StAXParser를 이용해서 XML 문서를 해석할 때 기본적으로 이 틀을 사용하면 될 것 같다.
반응형
댓글