Вопрос по java, xml – Как получить узел за узлом дерева XML, используя Xpath?

3

Во-первых, я должен сказать, что я нахожуXpath как очень хороший синтаксический анализатор, и я думаю, что он довольно мощный, если сравнивать его с другими парсерами.

Дайте следующий код:

<code>  DocumentBuilderFactory domFactory = 
  DocumentBuilderFactory.newInstance();
  domFactory.setNamespaceAware(true); 
  DocumentBuilder builder = domFactory.newDocumentBuilder();
  Document doc = builder.parse("input.xml");
  XPath xpath = XPathFactory.newInstance().newXPath();
</code>

Если бы я хотел найтиfirst узел раунда 1 и двери 1, здесь:

<code><Game>
    <Round>
        <roundNumber>1</roundNumber>
        <Door>
            <doorName>abd11</doorName>
            <Value>
                <xVal1>0</xVal1>
                <xVal2>25</xVal2>
                <pVal>0.31</pVal>
            </Value>
            <Value>
                <xVal1>25</xVal1>
                <xVal2>50</xVal2>
                <pVal>0.04</pVal>
            </Value>
            <Value>
                <xVal1>50</xVal1>
                <xVal2>75</xVal2>
                <pVal>0.19</pVal>
            </Value>
            <Value>
                <xVal1>75</xVal1>
                <xVal2>100</xVal2>
                <pVal>0.46</pVal>
            </Value>
        </Door>
        <Door>
            <doorName>vvv1133</doorName>
            <Value>
                <xVal1>60</xVal1>
                <xVal2>62</xVal2>
                <pVal>1.0</pVal>
            </Value>
        </Door>
    </Round>
    <Round>
        <roundNumber>2</roundNumber>
        <Door>
            <doorName>eee</doorName>
            <Value>
                <xVal1>0</xVal1>
                <xVal2>-25</xVal2>
                <pVal>0.31</pVal>
            </Value>
            <Value>
                <xVal1>-25</xVal1>
                <xVal2>-50</xVal2>
                <pVal>0.04</pVal>
            </Value>
            <Value>
                <xVal1>-50</xVal1>
                <xVal2>-75</xVal2>
                <pVal>0.19</pVal>
            </Value>
            <Value>
                <xVal1>-75</xVal1>
                <xVal2>-100</xVal2>
                <pVal>0.46</pVal>
            </Value>
        </Door>
        <Door>
            <doorName>cc</doorName>
            <Value>
                <xVal1>-60</xVal1>
                <xVal2>-62</xVal2>
                <pVal>0.3</pVal>
            </Value>
            <Value>
                <xVal1>-70</xVal1>
                <xVal2>-78</xVal2>
                <pVal>0.7</pVal>
            </Value>
        </Door>
    </Round>
</Game>
</code>

Я сделаю это:

<code> XPathExpression expr = xpath.compile("//Round[1]/Door[1]/Value[1]/*/text()");      
  Object result = expr.evaluate(doc, XPathConstants.NODESET);
  NodeList nodes = (NodeList) result;
</code>

и если бы я хотелsecond узел раунда 1 и двери 1, то:

<code>XPathExpression expr = xpath.compile("//Round[1]/Door[1]/Value[2]/*/text()");  
</code>

но как мне сделать это с помощью цикла, так как я не знаю, сколькоValue-nodes Я имею в виду, как я могу сделать это, используя цикл, где каждую итерацию я получаю 3 (я имею в видуxVal1 , xVal2 а такжеpVal values) больше значений узла Value!?

Причины для этого:

Я не знаю сколькоRound меня есть

Я не знаю сколькоValue меня есть

Я не хочу объявлять каждый раз новыйXPathExpression

Благодарность

@ ron: это один из самых распространенных вопросов и ответов по XPath: use:count((//Round/Door)[1]/Value) а потомcount((//Round/Door)[$k]/Value) Dimitre Novatchev
Используйте XPathcount() функция, чтобы найти количество узлов, затем использовать (вложенный) цикл: Dimitre Novatchev
@ DimitreNovatchev: я пробовал считать, но он не работает, посмотрите: если я напишу следующее: "double count = (Double) xpath.evaluate (" count (// Round / Door [1] / Value) ", doc, XPathConstants.NUMBER);" тогда я получаю "count == 8" ......... если я пишу вместо "int k = 1; double count = (Double) xpath.evaluate (" count (// Round / Door [k] / Значение) ", doc, XPathConstants.NUMBER);" тогда я получаю "count == 0" ron
@ erikxiv: Проблема в том, что в каждом раунде есть 2 поля, одно из которых «roundNumber», а второе «Дверь». Теперь с помощью одного запроса, то есть XPathExpression, я могу получить только один, но не оба? потому что из того, что я вижу в данный момент, я должен сделать 2 запроса - один для поля "roundNumber" и другой для "Дверь" каждого раунда ... и т. д. ron
Почему бы не несколько экспрессов? Разве это не то, что вы хотите (например, сначала получите список раундов, затем дверей, затем значений?) erikxiv

Ваш Ответ

1   ответ
7

Опция - перебирать все элементы Value в документе. Требуется только одна оценка, но трудно понять, какому элементу Round или Door принадлежит значение.

//Round/Door/Value/*/text()", doc, XPathConstants.NODESET);

Вариант 2 - перебирать каждый элемент Round, Door и Value отдельно. Требуется больше оценок, но контекст легко известен. Если требуется индекс, легко добавить счетчик в циклы.

// Get all rounds and iterate over them
NodeList rounds = (NodeList) xpath.evaluate("//Round", doc, XPathConstants.NODESET);
for (Node round : rounds) {
  // Get all doors and iterate over them
  NodeList doors = (NodeList) xpath.evaluate("Door", round, XPathConstants.NODESET);
  for (Node door : doors) {
    // Get all values and iterate over them
    NodeList values = (NodeList) xpath.evaluate("Value/*/text()", door, XPathConstants.NODESET);
    for (Node value : values) {
      // Do something
    }
  }
}

Вариант 3 - Сочетайте вышеперечисленное в зависимости от ваших требований

Обратите внимание, что я удалил шаг компиляции выражений, чтобы сократить пример. Это должно быть добавлено повторно для улучшения производительности.

@ majorbanzai Вы думаете об улучшенном синтаксисе for-loop? Я подозреваю, что вы правы в том, что пример кода не будет работать какNodeList это неIterable. Делает ли этот пример более читабельным, хотя ... erikxiv
Nodelist не может повторяться таким образом. viruskimera
КоторыйNodeList класс вы используете это реализуетIterable? majorbanzai

Похожие вопросы