При анализе большого XML-файла с помощью SAX-парсера класс становится раздутым и нечитаемым. Как это исправить? - PullRequest
3 голосов
/ 26 августа 2010

Это чисто вопрос, связанный с читабельностью кода, производительность класса не является проблемой.

Вот как я строю этот XMLHandler:

Для каждого элемента, который имеет отношение кв приложении у меня есть логическое значение в 'ElementName', которое я установил в true или false в зависимости от моего местоположения во время синтаксического анализа: Проблема, у меня теперь есть логическое объявление 10+ в начале моего класса, и оно становится все больше и больше.

В моем startElement и в моем методе endElement у меня есть сотни строк

if (qName = "elementName") {
   ...
} else if (qName = "anotherElementName") {
   ...
}

с различными правилами синтаксического анализа в них (если я нахожусь в этой позиции в файле xml, сделайте это,в противном случае, сделайте это и т. д.мой код более читабелен?

Ответы [ 3 ]

2 голосов
/ 26 августа 2010

Для чего вы используете логические переменные?Чтобы отслеживать вложение?

Недавно я реализовал это, используя перечисление для каждого элемента.Код работает, но это грубое приближение к нему: «1003 *

enum Element {
   // special markers:
   ROOT,
   DONT_CARE,

   // Element               tag                  parents
   RootElement(             "root"               ROOT),
   AnElement(               "anelement"),     // DONT_CARE
   AnotherElement(          "anotherelement"),// DONT_CARE
   AChild(                  "child",             AnElement),
   AnotherChild(            "child",             AnotherElement);

   Element() {...}
   Element(String tag, Element ... parents) {...}
}

class MySaxParser extends DefaultHandler {
    Map<Pair<Element, String>, Element> elementMap = buildElementMap();
    LinkedList<Element> nestingStack = new LinkedList<Element>();

    public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) {
        Element parent = nestingStack.isEmpty() ? ROOT : nestingStack.lastElement();
        Element element = elementMap.get(pair(parent, sName));
        if (element == null)
            element = elementMap.get(DONT_CARE, sName);
        if (element == null)
            throw new IllegalStateException("I did not expect <" + sName + "> in this context");

        nestingStack.addLast(element);

        switch (element) {
        case RootElement: ... // Probably don't need cases for many elements at start unless we have attributes
        case AnElement: ...
        case AnotherElement: ...
        case AChild: ...
        case AnotherChild: ...
        default: // Most cases here. Generally nothing to do on startElement
        }
    }
    public void endElement(String namespaceURI, String sName, String qName) {
        // Similar to startElement() but most switch cases do something with the data.
        Element element = nestingStack.removeLast();
        if (!element.tag.equals(sName)) throw IllegalStateException();
        switch (element) {
           ...
        }
    }

    // Construct the structure map from the parent information.
    private Map<Pair<Element, String>, Element> buildElementMap() {
        Map<Pair<Element, String>, Element> result = new LinkedHashMap<Pair<Element, String>, Element>();
        for (Element element: Element.values()) {
            if (element.tag == null) continue;
            if (element.parents.length == 0)
                result.put(pair(DONT_CARE, element.tag), element);
            else for (Element parent: element.parents) {
                result.put(pair(parent, element.tag), element);
            }
        }
        return result;
    }
    // Convenience method to avoid the need for using "new Pair()" with verbose Type parameters 
    private <A,B> Pair<A,B> pair(A a, B b) {
        return new Pair<A, B>(a, b);
    }
    // A simple Pair class, just for completeness.  Better to use an existing implementation.
    private static class Pair<A,B> {
        final A a;
        final B b;
        Pair(A a, B b){ this.a = a; this.b = b;}
        public boolean equals(Object o) {...};
        public int hashCode() {...};
    }
}

* 1006» Редактировать:
Позиция в структуре XML отслеживаетсястек элементов.Когда вызывается startElement, соответствующее Element перечисление может быть определено с использованием 1) родительского элемента из стека отслеживания и 2) тега элемента, переданного в качестве параметра sName в качестве ключа для карты, сгенерированной из родительской информации, определенной как частьперечисления ElementКласс Pair является просто держателем ключа из двух частей.

Этот подход позволяет одному и тому же элементу-тегу, который неоднократно встречается в разных частях структуры XML с различной семантикой, быть представленным различными Element перечисления.Например:

<root>
  <anelement>
    <child>Data pertaining to child of anelement</child>
  </anelement>      
  <anotherelement>
    <child>Data pertaining to child of anotherelement</child>
  </anotherelement>
</root>

Используя эту технику, нам не нужно использовать флаги для отслеживания контекста, чтобы мы знали, какой элемент <child> обрабатывается.Контекст объявляется как часть определения перечисления Element и уменьшает путаницу за счет исключения различных переменных состояния.

0 голосов
/ 26 августа 2010

Я бы отступил на JAXB или что-то подобное и позволил бы фреймворку сделать всю работу.

0 голосов
/ 26 августа 2010

Это зависит от структуры XML. Если действия для разных случаев просты или (более или менее) «независимы», вы можете попробовать использовать карту:

interface Command {
   public void assemble(Attributes attr, MyStructure myStructure);
}
...

Map<String, Command> commands= new HashMap<String, Command>();
...
if(commands.contains(qName)) {
   commands.get(qname).assemble(attr, myStructur);
} else {
   //unknown qName
}
...