[英]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.