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