2017-07-19 32 views
1

我試圖使用XPath表達式來查找引用VTD-XML中當前元素的元素。所以說我的XML包含書籍和評級,看起來像這樣:僅使用XPath的VTD-XML中的動態查找

<root> 
    <book id="1" name="Book1"/> 
    <book id="2" name="Book1"/> 
    <rating book-id="1" value="5"/> 
    <rating book-id="2" value="3"/> 
</root> 

首先我遍歷所有書籍元素。然後,對於每本書,我想執行一個獲取該書評分的XPath表達式。例如:

/root/rating[@book-id=current()/@id]/@value 

這不起作用,因爲current()函數是XSLT所獨有的。所以我試着將一個名爲「current」的變量表達式聲明爲「。」意思是「當前書」,但這不起作用,或者是因爲(顧名思義),一個變量表達式不會存儲表達式的結果,而是表達式本身。

有沒有一種方法可以使用XPath表達式在VTD-XML中實現這種效果? (我意識到人們在代碼中做的各種方式,但我想用純XPath的,因此用戶可以很容易地創建一個配置文件描述他們的數據格式)

編輯: 接受的答案的結果是,我想要使​​用單個XPath表達式無法完成。最後我加入一個選項,讓用戶基本上可以指定如何對當前圖書的唯一標識,可以發現(即「./@id」,也許「./isbn」)。然後,我的代碼執行此表達式,並將結果替換爲評級搜索XPath中的某個佔位符(例如「$$」)。

+0

我在vtd-xml-users列表上看到了您的帖子,有什麼沒有回答? –

+0

不,我的問題已得到解答。我明天會發送一個後續鏈接到這篇文章。順便說一下,感謝VTD-XML,這太棒了! – zwets

回答

2

//*/rating[./@book-id=//book/@id]/@value一個XPath表達式應該只爲匹配上提供的書籍ID的那個收視率檢索評價值。

如果您將<rating book-id="3" value="4"/>添加到XML文檔,那麼XPath將只返回書1和2的值53,因爲沒有可用的ID 3的書。

一個簡單的測試方法與VTD看起來是這樣的:

@Test 
public void xpathReference() throws Exception { 
    byte[] bytes = ("<root>\n" 
       + " <book id=\"1\" name=\"Book1\"/>\n" 
       + " <book id=\"2\" name=\"Book1\"/>\n" 
       + " <rating book-id=\"1\" value=\"5\"/>\n" 
       + " <rating book-id=\"2\" value=\"3\"/>\n" 
       + " <rating book-id=\"3\" value=\"4\"/>\n" 
       + "</root>").getBytes(); 

    VTDGen vtdGenerator = new VTDGen(); 
    vtdGenerator.setDoc(bytes); 
    vtdGenerator.parse(true); 
    VTDNav vtdNavigator = vtdGenerator.getNav(); 

    AutoPilot autoPilot = new AutoPilot(vtdNavigator); 
    autoPilot.selectXPath("//*/rating[./@book-id=//book/@id]/@value"); 
    int id; 
    int count = 0; 
    while ((id = autoPilot.evalXPath()) != -1) { 
     String elementName = vtdNavigator.toString(id); 
     int text = vtdNavigator.getAttrVal(elementName); 
     String txt = text != -1 ? vtdNavigator.toNormalizedString(text) : ""; 
     System.out.println("Found match at ID " + id + " in field name '" + elementName + "' with value '" + txt + "'"); 
     count++; 
    } 
    System.out.println("Total number of matches: " + count); 
    assertThat(count, is(equalTo(2))); 
} 

上執行這種測試方法,你應該會看到類似下面的輸出:

Found match at ID 15 in field name 'value' with value '5' 
Found match at ID 20 in field name 'value' with value '3' 
Total number of matches: 2 

按照評論上面的代碼沒有像迭代那樣以迭代方式提取當前處理的書籍的數據。下面現在的代碼試圖實現這一目標:

@Test 
public void xpathReference() throws Exception { 
    byte[] bytes = ("<root>\n" 
        + " <book id=\"1\" name=\"Book1\"/>\n" 
        + " <book id=\"2\" name=\"Book2\"/>\n" 
        + " <book id=\"4\" name=\"Book3\"/>\n" 
        + " <rating book-id=\"1\" value=\"5\"/>\n" 
        + " <rating book-id=\"2\" value=\"3\"/>\n" 
        + " <rating book-id=\"3\" value=\"4\"/>\n" 
        + "</root>").getBytes(); 

    VTDGen vtdGenerator = new VTDGen(); 
    vtdGenerator.setDoc(bytes); 
    vtdGenerator.parse(true); 
    VTDNav vtdNavigator = vtdGenerator.getNav(); 

    AutoPilot autoPilot = new AutoPilot(vtdNavigator); 
    autoPilot.selectXPath("//book/@id"); 
    int id; 
    int count = 0; 
    while ((id = autoPilot.evalXPath()) != -1) { 
     String elementName = vtdNavigator.toString(id); 
     int bookId_id = vtdNavigator.getAttrVal(elementName); 
     String bookId = bookId_id != -1 ? vtdNavigator.toNormalizedString(bookId_id) : ""; 

     AutoPilot xpathBookName = new AutoPilot(vtdNavigator); 
     xpathBookName.selectXPath("//book[@id=" + bookId + "]/@name"); 
     String bookName = xpathBookName.evalXPathToString(); 

     AutoPilot xpathRating = new AutoPilot(vtdNavigator); 
     xpathRating.selectXPath("//rating[@book-id=" + bookId + "]/@value"); 
     String bookRating = xpathRating.evalXPathToString(); 

     if ("".equals(bookRating)) { 
      System.out.println("Book " + bookName + " with id " + bookId + " has no rating yet"); 
     } else { 
      System.out.println("Book " + bookName + " with id " + bookId + " has a rating of " + bookRating); 
     } 
     count++; 
    } 
    System.out.println("Total number of matches: " + count); 
    assertThat(count, is(equalTo(3))); 
} 

如果執行後面的代碼,你應該看到類似的輸出:

Book Book1 with id 1 has a rating of 5 
Book Book2 with id 2 has a rating of 3 
Book Book3 with id 4 has no rating yet 
Total number of matches: 2 

請注意,我也稍微更新您的第二本書的名稱,以便您可以更容易地看到差異。


...是的,只需在Java代碼中獲取當前書籍的id,然後使用該代碼構建XPath表達式就很容易,但正如我所解釋的,我希望用戶能夠使用XPath來定義其文檔格式,所以我不希望代碼中的任何格式特定的東西

VTD只支持XPath 1.0。如果您(或您的客戶)能夠提出XPath 1.0查詢,則您應該能夠通過VTD提取相應的值。我猜想,普通的XPath查詢不足以表達您的需求。

作爲一個例子,可能對於您需要的用例很簡單,但很難給出關於如何設計應用程序來處理這種場景的任何建議。也許用更詳細的例子更新你的問題。你可以處理這個問題的一個簡單方法是引入必須單獨定義的佔位符變量,然後在試圖執行這種XPath表達式時點擊這些佔位符,以便簡單地用先前提取的值的具體值替換這些佔位符。

+0

感謝您的回答,但我認爲您錯過了我正在迭代書籍並希望僅使用XPath獲得* current *書的評分的事實。 (是的,只需要在Java代碼中獲得當前書籍的id,然後用它構造一個XPath表達式就很容易了,但正如我所解釋的,我希望用戶能夠使用XPath來定義它們的文檔格式,所以我不想在代碼中使用任何格式特定的東西) – zwets

+0

@zwets更新帖子以使用當前處理的書籍來提取姓名和評分 –

+0

謝謝!我採用了類似於您在帖子中提出的建議,實質上實現了我自己的變量系統。順便說一句,只是一個建議,但你可能會在答案的頂部添加一個簡短的註釋,歸結爲「不,這是不可能的,但請參閱下面的建議」。 – zwets