簡體   English   中英

Java XML JDOM2 XPath-使用XPath表達式從XML屬性和元素讀取文本值

[英]Java XML JDOM2 XPath - Read text value from XML attribute and element using XPath expression

應該允許該程序使用XPath表達式從XML文件讀取。 我已經使用JDOM2啟動了該項目,不需要切換到另一個API。 困難在於,該程序事先不知道是否必須讀取元素或屬性。 API是否僅通過為其提供XPath表達式就提供了接收內容(字符串)的任何功能? 根據我對JDOM2中的XPath的了解,它使用不同類型的對象來評估指向屬性或元素的XPath表達式。 我只對XPath表達式指向的屬性/元素的內容感興趣。

這是一個示例XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
  <book category="COOKING">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book category="CHILDREN">
    <title lang="en">Harry Potter</title>
    <author>J K. Rowling</author>
    <year>2005</year>
    <price>29.99</price>
  </book>
  <book category="WEB">
    <title lang="en">XQuery Kick Start</title>
    <author>James McGovern</author>
    <author>Per Bothner</author>
    <author>Kurt Cagle</author>
    <author>James Linn</author>
    <author>Vaidyanathan Nagarajan</author>
    <year>2003</year>
    <price>49.99</price>
  </book>
  <book category="WEB">
    <title lang="en">Learning XML</title>
    <author>Erik T. Ray</author>
    <year>2003</year>
    <price>39.95</price>
  </book>
</bookstore>

這是我的程序的樣子:

package exampleprojectgroup;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.filter.Filters;
import org.jdom2.input.SAXBuilder;
import org.jdom2.input.sax.XMLReaders;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;


public class ElementAttribute2String
{
    ElementAttribute2String()
    {
        run();
    }

    public void run()
    {
        final String PATH_TO_FILE = "c:\\readme.xml";
        /* It is essential that the program has to work with a variable amount of XPath expressions. */
        LinkedList<String> xPathExpressions = new LinkedList<>();
        /* Simulate user input.
         * First XPath expression points to attribute,
         * second one points to element.
         * Many more expressions follow in a real situation.
         */
        xPathExpressions.add( "/bookstore/book/@category" );
        xPathExpressions.add( "/bookstore/book/price" );

        /* One list should be sufficient to store the result. */
        List<Element> elementsResult = null;
        List<Attribute> attributesResult = null;
        List<Object> objectsResult = null;
        try
        {
            SAXBuilder saxBuilder = new SAXBuilder( XMLReaders.NONVALIDATING );
            Document document = saxBuilder.build( PATH_TO_FILE );
            XPathFactory xPathFactory = XPathFactory.instance();
            int i = 0;
            for ( String string : xPathExpressions )
            {
                /* Works only for elements, uncomment to give it a try. */
//                XPathExpression<Element> xPathToElement = xPathFactory.compile( xPathExpressions.get( i ), Filters.element() );
//                elementsResult = xPathToElement.evaluate( document );
//                for ( Element element : elementsResult )
//                {
//                    System.out.println( "Content of " + string + ": " + element.getText() );
//                }

                /* Works only for attributes, uncomment to give it a try. */
//                XPathExpression<Attribute> xPathToAttribute = xPathFactory.compile( xPathExpressions.get( i ), Filters.attribute() );
//                attributesResult = xPathToAttribute.evaluate( document );
//                for ( Attribute attribute : attributesResult )
//                {
//                    System.out.println( "Content of " + string + ": " + attribute.getValue() );
//                }

                /* I want to receive the content of the XPath expression as a string
                 * without having to know if it is an attribute or element beforehand.
                 */
                XPathExpression<Object> xPathExpression = xPathFactory.compile( xPathExpressions.get( i ) );
                objectsResult = xPathExpression.evaluate( document );
                for ( Object object : objectsResult )
                {
                    if ( object instanceof Attribute )
                    {
                        System.out.println( "Content of " + string + ": " + ((Attribute)object).getValue() );
                    }
                    else if ( object instanceof Element )
                    {
                        System.out.println( "Content of " + string + ": " + ((Element)object).getText() );
                    }
                }
                i++;
            }
        }
        catch ( IOException ioException )
        {
            ioException.printStackTrace();
        }
        catch ( JDOMException jdomException )
        {
            jdomException.printStackTrace();
        }
    }
}

另一個想法是在XPath表達式中搜索'@'字符,以確定它是否指向屬性或元素。 盡管我希望有一個更優雅的解決方案,但這給了我想要的結果。 JDOM2 API是否提供任何有用的解決此問題的方法? 可以重新設計代碼以滿足我的要求嗎?

先感謝您!

XPath表達式很難鍵入/發布,因為它們需要在對表達式中XPath函數/值的返回類型敏感的系統中進行編譯。 JDOM依靠第三方代碼來執行此操作,並且該第三方代碼沒有在JDOM代碼的編譯時將這些類型關聯的機制。 請注意,XPath表達式可以返回許多不同類型的內容,包括字符串,布爾值,數字和類似節點列表的內容。

在大多數情況下,在評估表達式之前,XPath表達式返回類型是已知的,並且程序員具有“正確”的強制轉換/期望來處理結果。

在您的情況下,您不需要這樣做,並且表達式更加動態。

我建議您聲明一個輔助函數來處理內容:

private static final Function extractValue(Object source) {
    if (source instanceof Attribute) {
        return ((Attribute)source).getValue();
    }
    if (source instanceof Content) {
        return ((Content)source).getValue();
    }
    return String.valueOf(source);
} 

至少這將使您的代碼更加整潔,並且如果您使用Java8流,則可能會非常緊湊:

List<String> values = xPathExpression.evaluate( document )
                      .stream()
                      .map(o -> extractValue(o))
                      .collect(Collectors.toList());

請注意,Element節點的XPath規范是string-value是Element的text()內容以及所有子元素的內容的疊加。 因此,在以下XML代碼段中:

<a>bilbo <b>samwise</b> frodo</a>

a元素上的getValue()將返回bilbo samwise frodo ,但getText()將返回bilbo frodo 仔細選擇用於價值提取的機制。

我遇到了完全相同的問題,並采取了一種方法來識別屬性何時是Xpath的焦點。 我解決了兩個功能。 第一個編譯了XPathExpression供以后使用:

    XPathExpression xpExpression;
    if (xpath.matches(  ".*/@[\\w]++$")) {
        // must be an attribute value we're after.. 
        xpExpression = xpfac.compile(xpath, Filters.attribute(), null, myNSpace);
    } else { 
        xpExpression = xpfac.compile(xpath, Filters.element(), null, myNSpace);
    }

第二個計算並返回一個值:

Object target = xpExpression.evaluateFirst(baseEl);
if (target != null) {
    String value = null;
    if (target instanceof Element) {
        Element targetEl = (Element) target;
        value = targetEl.getTextNormalize();
    } else if (target instanceof Attribute) {
        Attribute targetAt = (Attribute) target;
        value = targetAt.getValue();
    }

我懷疑這是一種編碼風格問題,無論您是喜歡上一個答案中建議的輔助函數還是這種方法。 兩者都會起作用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM