Определение, находится ли на листовом узле парсер SAX - PullRequest
12 голосов
/ 12 апреля 2019

Используя org.xml.sax.helpers.DefaultHandler, можете ли вы определить, находитесь ли вы в листовом узле в endElement(String, String, String)?

Или вам нужно использовать DOM-парсер, чтобы определить это?

Ответы [ 2 ]

10 голосов
/ 15 апреля 2019

Давайте начнем с некоторых основных определений:

XML-документ - это упорядоченное, помеченное дерево. Каждый узел дерева является элементом XML и записывается с открывающим и закрывающим тегом.

(из здесь ). Самое замечательное в этом: это означает, что XML-файлы имеют очень простую и регулярную структуру. Например, определение узла leaf таково: узел, у которого нет дочерних элементов.

Теперь: этот метод endElement() вызывается всякий раз, когда синтаксический анализатор SAX встречает закрывающий тег узла. Предполагая, что ваш XML имеет допустимое содержимое, это также означает, что синтаксический анализатор дал вам соответствующий вызов startElement() ранее!

Другими словами: вам доступна вся информация, необходимая для определения «конечного» конечного узла:

  • вам сказали, какие элементы "запущены"
  • вам говорят, какие элементы заканчиваются

Возьмите этот пример:

<outer>
  <inner/>
</outer>

Это приведет к такой последовательности событий / обратных вызовов:

  • событие: начальный элемент внешний
  • событие: начальный элемент внутренний
  • событие: конечный элемент внутренний
  • событие: конечный элемент внешний

Итак, «очевидно», когда ваш синтаксический анализатор запоминает историю событий, определение того, какой из inner или outer является листовым узлом, прямо вперед!

Таким образом, ответ таков: нет, вам не нужен анализатор DOM. В конце концов, DOM все равно строится из той же информации! Если анализатор DOM может определить «объем» объектов, то и ваш SAX-анализатор может.

Но только для справки: вам все еще нужно тщательно реализовать свои структуры данных, которые отслеживают теги «начато», «открыто» и «закончено», например, чтобы правильно определить, что это:

<outer> <inner> <inner/> </inner> </outer>

представляет два нествольных листа (outer и первый inner) и один листовой узел (внутренний inner).

4 голосов
/ 15 апреля 2019

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

Этот флаг можно многократно сбрасывать всякий раз, когда применяется startElement.

Если несколько конечных узлов находятся на одном уровне, вы получите последовательные isLeafNode флаги.

Логическое обоснование этого может быть рассмотрено, если представить XML как стек.startElements толкает в стек.Первым всплывающим стеком после пуша будет листовой узел.Последующие всплывающие окна не будут листами, но они сбрасываются, если выполняется еще одно нажатие.

private boolean isLeafNode = false;

public void startElement(String uri, String localName, String qName, Attributes attributes) {
    isLeafNode = true;
}

public void endElement(String uri, String localName, String qName) {
    if(isLeafNode) {
        //do leaf node logic
    }

    isLeafNode = false;
}

Итак, для следующего XML листовые узлы выглядят следующим образом.

<foo>
    <bar>Leaf</bar>
    <baz>
        <bop>Leaf</bop>
        <beep>Leaf</beep>
        <blip>
            <moo>Leaf</moo>
        </blip>
    </baz>
</foo>
...