繁体   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