rozwiązanie bez korzystania z opakowania zewnętrznego, a nawet XPath: Stosuje "PARSE_MODE" enum
, prawdopodobnie w połączeniu z Stack<PARSE_MODE>
:
1) Podstawowy roztwór:
a) pola
private PARSE_MODE parseMode = PARSE_MODE.__UNDEFINED__;
// NB: essential that all these enum values are upper case, but this is the convention anyway
private enum PARSE_MODE {
__UNDEFINED__, ORDER, DATE, CUSTOMERID, ITEM };
private List<String> parseModeStrings = new ArrayList<String>();
private Stack<PARSE_MODE> modeBreadcrumbs = new Stack<PARSE_MODE>();
b) dokonać List<String>
, może w konstr Tor:
for(PARSE_MODE pm : PARSE_MODE.values()){
// might want to check here that these are indeed upper case
parseModeStrings.add(pm.name());
}
c) startElement
i endElement
:
@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
String localNameUC = localName.toUpperCase();
// pushing "__UNDEFINED__" would mess things up! But unlikely name for an XML element
assert ! localNameUC.equals("__UNDEFINED__");
if(parseModeStrings.contains(localNameUC)){
parseMode = PARSE_MODE.valueOf(localNameUC);
// any "policing" to do with which modes are allowed to switch into
// other modes could be put here...
// in your case, go `new Order()` here when parseMode == ORDER
modeBreadcrumbs.push(parseMode);
}
else {
// typically ignore the start of this element...
}
}
@Override
private void endElement(String uri, String localName, String qName) throws Exception {
String localNameUC = localName.toUpperCase();
if(parseModeStrings.contains(localNameUC)){
// will not fail unless XML structure which is malformed in some way
// or coding error in use of the Stack, etc.:
assert modeBreadcrumbs.pop() == parseMode;
if(modeBreadcrumbs.empty()){
parseMode = PARSE_MODE.__UNDEFINED__;
}
else {
parseMode = modeBreadcrumbs.peek();
}
}
else {
// typically ignore the end of this element...
}
}
... więc co to wszystko znaczy? W dowolnym momencie masz wiedzę na temat "trybu parsowania", w którym się znajdujesz ... i możesz także spojrzeć na Stack<PARSE_MODE> modeBreadcrumbs
, jeśli chcesz dowiedzieć się, jakie inne tryby analizy parsowej przeszedłeś, aby uzyskać tutaj ...
Twoja metoda characters
staje się znacznie czystsze:
public void characters(char[] ch, int start, int length) throws SAXException {
switch(parseMode){
case DATE:
// PS - this SimpleDateFormat object can be a field: it doesn't need to be created hundreds of times
SimpleDateFormat formatter. ...
String value = ...
...
break;
case CUSTOMERID:
order.setCustomerId(...
break;
case ITEM:
item = new Item();
// this next line probably won't be needed: when you get to endElement, if
// parseMode is ITEM, the previous mode will be restored automatically
// isItem = false ;
}
}
2) Im bardziej „profesjonalne” rozwiązanie:
abstract
klasa która betonu klasy mają rozszerzyć i który następnie nie mają zdolność do modyfikowania Stack
, etc. NB to jest analizowane jako qName
zamiast localName
. Zatem:
public abstract class AbstractSAXHandler extends DefaultHandler {
protected enum PARSE_MODE implements SAXHandlerParseMode {
__UNDEFINED__
};
// abstract: the concrete subclasses must populate...
abstract protected Collection<Enum<?>> getPossibleModes();
//
private Stack<SAXHandlerParseMode> modeBreadcrumbs = new Stack<SAXHandlerParseMode>();
private Collection<Enum<?>> possibleModes;
private Map<String, Enum<?>> nameToEnumMap;
private Map<String, Enum<?>> getNameToEnumMap(){
// lazy creation and population of map
if(nameToEnumMap == null){
if(possibleModes == null){
possibleModes = getPossibleModes();
}
nameToEnumMap = new HashMap<String, Enum<?>>();
for(Enum<?> possibleMode : possibleModes){
nameToEnumMap.put(possibleMode.name(), possibleMode);
}
}
return nameToEnumMap;
}
protected boolean isLegitimateModeName(String name){
return getNameToEnumMap().containsKey(name);
}
protected SAXHandlerParseMode getParseMode() {
return modeBreadcrumbs.isEmpty()? PARSE_MODE.__UNDEFINED__ : modeBreadcrumbs.peek();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
try {
_startElement(uri, localName, qName, attributes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// override in subclasses (NB I think caught Exceptions are not a brilliant design choice in Java)
protected void _startElement(String uri, String localName, String qName, Attributes attributes)
throws Exception {
String qNameUC = qName.toUpperCase();
// very undesirable ever to push "UNDEFINED"! But unlikely name for an XML element
assert !qNameUC.equals("__UNDEFINED__") : "Encountered XML element with qName \"__UNDEFINED__\"!";
if(getNameToEnumMap().containsKey(qNameUC)){
Enum<?> newMode = getNameToEnumMap().get(qNameUC);
modeBreadcrumbs.push((SAXHandlerParseMode)newMode);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
try {
_endElement(uri, localName, qName);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// override in subclasses
protected void _endElement(String uri, String localName, String qName) throws Exception {
String qNameUC = qName.toUpperCase();
if(getNameToEnumMap().containsKey(qNameUC)){
modeBreadcrumbs.pop();
}
}
public List<?> showModeBreadcrumbs(){
return org.apache.commons.collections4.ListUtils.unmodifiableList(modeBreadcrumbs);
}
}
interface SAXHandlerParseMode {
}
Następnie Istotną częścią konkretnej podklasy:
private enum PARSE_MODE implements SAXHandlerParseMode {
ORDER, DATE, CUSTOMERID, ITEM
};
private Collection<Enum<?>> possibleModes;
@Override
protected Collection<Enum<?>> getPossibleModes() {
// lazy initiation
if (possibleModes == null) {
List<SAXHandlerParseMode> parseModes = new ArrayList<SAXHandlerParseMode>(Arrays.asList(PARSE_MODE.values()));
possibleModes = new ArrayList<Enum<?>>();
for(SAXHandlerParseMode parseMode : parseModes){
possibleModes.add(PARSE_MODE.valueOf(parseMode.toString()));
}
// __UNDEFINED__ mode (from abstract superclass) must be added afterwards
possibleModes.add(AbstractSAXHandler.PARSE_MODE.__UNDEFINED__);
}
return possibleModes;
}
PS jest to punkt wyjścia do bardziej wyrafinowanych rzeczy: na przykład można założyć List<Object>
który jest zsynchronizowany z Stack<PARSE_MODE>
: Objects
może być dowolna, umożliwiając "powrót do" wstępnych "węzłów XML" tego, z którym masz do czynienia. Nie należy jednak używać urządzenia Map
: obiekt Stack
może zawierać ten sam obiekt PARSE_MODE
więcej niż jeden raz. To w rzeczywistości przedstawia zasadniczą cechą wszystkich struktur drzewiastych, takich jak: żadna jednostka węzła(tu: analizowania tryb)istnieje w izolacji: jego tożsamość jest zawsze określona przez całą ścieżkę prowadzącą do niego.
Możesz wypróbować StAX –
Jeśli masz koncertowy model danych, który generuje XML, powinienem rzucić okiem na XStream (http://xstream.codehaus.org/). Robi naprawdę fajną robotę serializacji danych do xml iz powrotem. –
Na temat, lubię zacząć od XSD i używania XmlBeans. Nieco starsze, znaczniki XML mają rozróżniać wielkie i małe litery, a ten kod je zepsuje. –