Solution без использования вне пакета, или даже XPath: использовать enum
"PARSE_MODE", возможно, в сочетании с Stack<PARSE_MODE>
:
1) Основной раствор:
а) поля
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>();
б) сделать свой List<String>
, может быть, в строи тор:
for(PARSE_MODE pm : PARSE_MODE.values()){
// might want to check here that these are indeed upper case
parseModeStrings.add(pm.name());
}
с) startElement
и 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...
}
}
... так что же все это значит? В любой момент вы знаете о «режиме синтаксического анализа», в котором вы находитесь, и вы также можете посмотреть на Stack<PARSE_MODE> modeBreadcrumbs
, если вам нужно выяснить, какие другие режимы синтаксического анализа вы прошли, чтобы добраться сюда ...
Ваш метод characters
становится существенно чище:
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) Чем больше «профессиональный» решение:
abstract
класс, конкретные классы должны распространяться и которые затем не имеют возможности изменять Stack
и т.д. NB это рассматривает qName
, а не localName
. Таким образом:
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 {
}
Затем выступающая частью конкретного подкласса:
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 это является отправной точкой для более сложных вещей: например, вы можете создать List<Object>
, который синхронизируется с Stack<PARSE_MODE>
: Objects
может быть тем, что вы хотите, что позволяет вам «вернуться» в восходящие «узлы XML» того, с которым вы имеете дело. Не используйте Map
, хотя: Stack
может потенциально содержать один и тот же объект PARSE_MODE
более одного раза. Это на самом деле иллюстрирует фундаментальную характеристику всех древовидных структур: нет отдельного узла(здесь: синтаксический режим)существует в изоляции: его идентичность всегда определяется весь путь, ведущий к ней.
Вы можете попробовать StAX –
Если у вас есть модель данных концерта, которая генерирует XML, я бы взглянул на XStream (http://xstream.codehaus.org/). Это действительно хорошая работа по сериализации данных в xml и обратно. –
По теме мне нравится начинать с XSD и использовать XmlBeans. Немного OT, теги XML должны быть чувствительны к регистру, и этот код нарушает это. –