개요
XML 문서는 계층 적 문서로, 동일한 요소 이름과 네임 스페이스가 여러 위치에서 다른 의미를 가지며 무한 깊이 (재귀 적)로 나타날 수 있습니다. 일반적으로 큰 문제에 대한 해결책은 작은 문제로 나누는 것입니다. XML 구문 분석의 맥락에서 이는 해당 XML과 관련된 메서드에서 XML의 특정 부분을 구문 분석하는 것을 의미합니다. 예를 들어, 하나의 논리는 주소를 구문 분석합니다.
<Address>
<Street>Odins vei</Street>
<Building>4</Building>
<Door>b</Door>
</Address>
즉, 방법이있을 것입니다
AddressType parseAddress(...);
또는
void parseAddress(...);
논리의 어딘가에 XML 입력 인수를 가져와 객체를 반환합니다 (B의 결과는 나중에 필드에서 가져올 수 있음).
SAX
SAX는 XML 이벤트를 '푸시' 하므로 XML 이벤트가 프로그램 / 데이터에서 어디에 속하는지 결정하는 것은 사용자에게 달려 있습니다.
// method in stock SAX handler
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
// .. your logic here for start element
}
'Building'시작 요소의 경우 실제로 Address를 구문 분석하고 있는지 확인한 다음 Address를 해석하는 작업이있는 메서드로 XML 이벤트를 라우팅해야합니다.
StAX
StAX는 XML 이벤트를 '풀링' 하므로 프로그램 / 데이터에서 XML 이벤트를 수신 할 위치를 결정하는 것은 사용자에게 달려 있습니다.
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
}
물론 주소를 해석하는 작업이있는 메서드에서 항상 'Building'이벤트를 받고 싶을 것입니다.
토론
SAX와 StAX의 차이점은 밀고 당기는 것입니다. 두 경우 모두 구문 분석 상태를 어떻게 든 처리해야합니다.
이는 SAX의 경우 일반적인 방법 B와 StAX의 경우 방법 A로 변환됩니다. 또한 SAX는 B 개별 XML 이벤트를 제공해야하는 반면 StAX는 A에 여러 이벤트를 제공 할 수 있습니다 (XMLStreamReader 인스턴스 전달).
따라서 B는 먼저 구문 분석의 이전 상태를 확인한 다음 각 개별 XML 이벤트를 처리 한 다음 상태를 필드에 저장합니다. 메서드 A는 만족할 때까지 XMLStreamReader에 여러 번 액세스하여 XML 이벤트를 한 번에 모두 처리 할 수 있습니다.
결론
StAX를 사용하면 XML 구조에 따라 구문 분석 (데이터 바인딩) 코드를 구조화 할 수 있습니다 . 따라서 SAX와 관련하여 '상태'는 StAX의 프로그램 흐름에서 암시 적이지만 SAX에서는 항상 일종의 상태 변수를 유지하고 대부분의 이벤트 호출에 대해 해당 상태에 따라 흐름을 라우팅해야합니다.
가장 간단한 문서를 제외한 모든 문서에 StAX를 권장합니다. 차라리 나중에 최적화로 SAX로 이동하십시오 (하지만 그때까지는 바이너리로 이동하고 싶을 것입니다).
StAX를 사용하여 구문 분석 할 때 다음 패턴을 따르십시오.
public MyDataBindingObject parse(..) {
XMLStreamReader reader = ....;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
break;
}
} while(reader.hasNext());
MyDataBindingObject object = new MyDataBindingObject();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("Whatever1")) {
WhateverObject child = parseSubTreeForWhatever(reader);
level --;
object.setWhatever(child);
}
if(level == 2) {
parseSubTreeForWhateverAtRelativeLevel2(reader);
level --;
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} while(level > 0);
return object;
}
따라서 하위 방법은 거의 동일한 접근 방식, 즉 계산 수준을 사용합니다.
private MySubTreeObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
MySubTreeObject object = new MySubTreeObject();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("Whatever2")) {
MyWhateverObject child = parseMySubelementTree(reader);
level --;
object.setWhatever(child);
}
if(level == 2) {
MyWhateverObject child = parseMySubelementTree(reader);
level --;
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} while(level > 0);
return object;
}
그리고 결국 기본 유형을 읽을 수준에 도달합니다.
private MySetterGetterObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
MySetterGetterObject myObject = new MySetterGetterObject();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("FirstName")) {
String text = reader.getElementText()
if(text.length() > 0) {
myObject.setName(text)
}
level--;
} else if(reader.getLocalName().equals("LastName")) {
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} while(level > 0);
return myObject;
}
이것은 매우 간단하며 오해의 여지가 없습니다. 레벨을 올바르게 낮추는 것을 잊지 마십시오.
A. 문자를 예상했지만 문자를 포함해야하는 일부 태그에 END_ELEMENT가있는 경우 (위 패턴에서) :
<Name>Thomas</Name>
대신이었다
<Name></Name>
누락 된 하위 트리도 마찬가지입니다. 아이디어를 얻을 수 있습니다.
B. 시작 요소에서 호출되고 해당 종료 요소 이후에 반환되는 하위 구문 분석 메서드를 호출 한 후, 즉 파서가 메서드 호출 이전보다 한 수준 아래에 있습니다 (위의 패턴).
이 접근 방식은보다 강력한 구현을 위해 '무시할 수있는'공백도 완전히 무시하는 방법에 유의하십시오.
파서
로 이동 Woodstox 대부분의 기능 또는 대한 Aaalto-XML 속도.