【问题标题】:Java: get xpath of element in org.w3c.dom documentJava:获取 org.w3c.dom 文档中元素的 xpath
【发布时间】:2011-07-13 06:31:09
【问题描述】:

我已经写了我想要实现的目标。但是,getElementIdx() 函数不会返回正确的计数。 getPreviousSibling() 有问题,但我不知道为什么。

public static String getElementXpath(DOMElement elt){
        String path = ""; 

        try{
            for (; elt != null; elt = (DOMElement) elt.getParentNode()){
                int idx = getElementIdx(elt);
                String xname = elt.getTagName().toString();

                if (idx >= 1) xname += "[" + idx + "]";
                path = "/" + xname + path;  
            }
        }catch(Exception ee){
        }
        return path;                            
    }

    public static int getElementIdx(DOMElement elt) {
      int count = 1;
      try{

         for (DOMElement sib = (DOMElement) elt.getNextSibling(); sib != null; sib = (DOMElement) sib.getNextSibling())
            {
                if(sib.getTagName().equals(elt.getTagName())){
                    count++;
                }
            }
      }catch(Exception ee){      
      }
        return count;
    }

【问题讨论】:

  • 请更详细地描述您想要获得的 XPath 格式,或者只是说明您想要函数返回的 XPath 表达式的用途。我注意到 JavaScript 函数专门处理 @id。您是否要特别注意@id?
  • 另外,在你的第一句话中,你写的是getElementByXpath(),而我认为你想要getXpathForElement() - 你能澄清一下吗?
  • 迈克尔,是的,是的。我想关注@id。所以我会得到如下的 xpath 格式 //duv[@id="meni"]/span/a[2]
  • com.collaxa.xml.XPathUtils.getXPathExprFromNode(Node) 这不是你要找的吗?
  • 对不起。第一条评论中的错误包。 com.ibm.wsdl.util.xml.getXPathExprFromNode(Node) 这不是你要找的吗?

标签: java dom


【解决方案1】:

您的标题谈到getPreviousSibling(),但您的代码只使用getNextSibling() - 为什么?我完全不明白你为什么要使用getNextSibling()...你想知道有多少同名元素出现在当前元素之前,而不是有多少元素出现在之后。

您正在捕获和吞下异常的事实也令人深感怀疑……您为什么要这样做?如果有异常,该方法不应该以异常终止吗?

您还应该考虑到getPreviousSibling 可能不会返回元素——例如,它可能会返回文本节点这一事实。你会想跳过那些 - 目前你会得到一个异常,这将终止循环并返回当前计数。

如果这些没有帮助,请发布一些示例 XML,指出一个节点,并说明代码当前返回的内容(以及发布您更新的代码)。仅仅说它没有返回正确的计数几乎不如说它确实返回什么以及你期望它返回什么有用。

编辑:这就是我希望代码的样子:

public static int getElementIndex(Element original) {
  int count = 1;

  for (Node node = original.getPreviousSibling(); node != null;
       node = node.getPreviousSibling()) {
    if (node instanceof Element) {
      Element element = (Element) node;
      if (element.getTagName().equals(original.getTagName()) {
        count++;
      }
    }
  }

  return count;
}

您也可以使用if (node.getNodeType() == Node.ELEMENT_NODE) 代替instanceof 测试。

【讨论】:

  • 是的。那是一个错字。我的意思是 getPreviousSibling() 只是。 nextsibling() 没有意义。基本上,该函数只返回 1。 getElementIdx(element) 应该计算所有先前的兄弟姐妹,即 ELEMENT_NODEpreviousSibling.tagname 匹配 element.tagname。所以我最终会得到 /html/body/p[3] 这样的东西。
  • @Kim:异常处理呢?如果第一个“前一个兄弟”不是一个元素,那就可以解释为什么你的计数是 1 ......并且由于错误的演员阵容将立即引发异常,你将只返回当前值。吞下异常几乎总是是个坏主意。另外,您使用 DOMElement 而不仅仅是 Element 有什么原因吗? IMO,最好尽可能坚持使用 w3c 类型。如果您可以将您的代码更改为一个简短但完整的程序,我们都可以运行,那也会有所帮助...
  • 是的,异常处理似乎不在这里。我只是希望 for 循环即使在引发异常时也能继续......是的,我知道异常会立即引发,这就是为什么我总是得到 1 并且堆栈跟踪表明它基本上是在抱怨错误的转换。 DOMElement 继承 org.w3c.dom.Element。它只是有额外的功能。我不确定赏金到期后我的问题会发生什么,它会被删除吗?我会尝试用单独的赏金再次问这个问题..
  • 问题是我不明白为什么或在哪里吞下了异常。我的意思是,当我没有捕捉到任何东西时,代码就会像没有出错一样运行。如何解除异常?
  • @Kim Jong Woo:在异常吞咽方面,我注意到您在两个代码级别吞下了所有异常。停止这样做:)
【解决方案2】:

Dom4j xpath 支持非常好,您可以通过提供 xpath 表达式来访问任何元素。
但是我不确定反过来是否正确,即是否给定一个元素,您是否可以派生 xpath 表达式。

请参阅http://www.docjar.com/projects/dom4j-1.6.1-code.html 的 api

注意避免使用 www.dom4j.org,它似乎已被某种垃圾链接农场劫持。

【讨论】:

  • 是的,这在 Dom4j 中是可能的:使用 Node.getUniquePath()。但是,您需要先将 W3C 文档转换为 Dom4j 文档。实际上,这很容易(只需使用new DOMReader().read(w3cDocument)),但这不是一个非常有效的解决方案,尤其是在必须重复进行转换的情况下。
  • 什么是更好的方法?现在,我只是想把这个 Javascript 函数翻译成 Java。 snippets.dzone.com/posts/show/3754
  • @Chris,如果反复使用 Dom4j 会怎样?会很慢还是浪费内存?
  • @Kim:不,如果您使用 Dom4j 而不是 W3C DOM 而不在它们之间进行转换,那么不会有性能损失。
  • @Chris,我正在翻译。我已经编写了一个从 org.w3c.dom.domdocument 构造基数 xpath 并读取 xpath 的函数的一半。我应该继续这个追求还是切换到 dom4j。如果重复翻译 w3c dom 文档的惩罚还不错,我还不如....
【解决方案3】:

我使用了 XOM library,它有一个很好的 API。步行执行此操作比在 XSLT 中更困难。以下内容将帮助您入门。请注意,缺少兄弟位置的东西。

一个接口:

package milu.calcxpath;
import nu.xom.Node;
import nu.xom.ParentNode;

public interface Calculator
{
    public void buildXPath( Node node, StringBuilder sb );
    public void buildXPath( ParentNode node, StringBuilder sb );
}

实现类:

package milu.calcxpath;
import nu.xom.Attribute;
import nu.xom.Comment;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Node;
import nu.xom.ParentNode;
import nu.xom.ProcessingInstruction;
import nu.xom.Text;

public class SimpleCalculator implements Calculator
{
    @Override
    public void buildXPath( Node node, StringBuilder sb )
    {
        if ( null == node )
            return;
        if ( this.findShortCut(node, sb) )
            return;

        ParentNode parent = node.getParent();
        boolean doParents = true;
        if ( parent instanceof Element )
            if ( this.findShortCut((Element) parent, sb) )
                doParents = false;
        if ( doParents )
            this.buildXPath(parent, sb);

        if ( node instanceof Element ) {
            String name = ( (Element) node ).getLocalName();
            sb.append("/" + name);
        } else if ( node instanceof Attribute ) {
            sb.append("/@" + ( (Attribute) node ).getLocalName());
        } else if ( node instanceof Text ) {
            sb.append("/text()");
        } else if ( node instanceof Comment ) {
            sb.append("/comment()");
        } else if ( node instanceof ProcessingInstruction ) {
            sb.append("/processing-instruction()");
        }
    }

    protected boolean findShortCut( Node node, StringBuilder sb )
    {
        return false;
    }

    @Override
    public void buildXPath( ParentNode node, StringBuilder sb )
    {
        if ( null == node )
            return;
        ParentNode parent = node.getParent();
        if ( null == parent )
            return;
        else if ( parent instanceof Document ) {
            ;
        } else { // element
            if ( ! this.findShortCut((Element) parent, sb) )
                this.buildXPath(parent, sb);
        }
        sb.append("/");
        sb.append(( (Element) node ).getLocalName());
    }

    protected boolean findShortCut( Element elm, StringBuilder sb )
    {
        return false;
    }
}

另一个,扩展它。这是@id 的东西。

package milu.calcxpath;
import nu.xom.Attribute;
import nu.xom.Element;
import nu.xom.Node;

public class IdShortCutCalculator extends SimpleCalculator
{
    final private static String ID = "id";

    @Override
    protected boolean findShortCut( Node node, StringBuilder sb )
    {
        if ( ! ( node instanceof Attribute ) )
            return false;
        Attribute attr = (Attribute) node;
        if ( ! attr.getLocalName().equals(ID) )
            return false;
        sb.append("//@id='");
        sb.append(attr.getValue());
        sb.append("'");
        return true;
    }

    @Override
    protected boolean findShortCut( Element elm, StringBuilder sb )
    {
        String val = elm.getAttributeValue(ID);
        if ( null == val )
            return false;
        sb.append("//*[@id='");
        sb.append(val);
        sb.append("']");
        return true;
    }
}

另一个类作为前端:

package milu.calcxpath;

import nu.xom.Node;

public class XPathCalculator
{
    private Calculator calculator;

    public XPathCalculator(Calculator calc) {
        this.calculator = calc;
    }

    public String calculateXPath( Node node )
    {
        StringBuilder sb = new StringBuilder();
        this.calculator.buildXPath(node, sb);
        return sb.toString();
    }
}

还有一个测试脚本:

package milu.calcxpath;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Nodes;

public class Test
{
    public static void main( String[] args ) throws Exception
    {
        Builder builder = new Builder();
        Document doc = builder.build(Test.class.getResourceAsStream("/milu/calcxpath/eins.xml"));
        Calculator calc;
        // calc = new SimpleCalculator();
        calc = new IdShortCutCalculator();
        XPathCalculator xpc = new XPathCalculator(calc);
        show(xpc, doc, "//*");
        show(xpc, doc, "//@*");
        show(xpc, doc, "//node()");
        show(xpc, doc, "//processing-instruction()");
        show(xpc, doc, "//*//processing-instruction()");
    }

    private static void show( XPathCalculator xpc, Document doc, String xpath )
    {
        System.out.println("==========================");
        System.out.println("    " + xpath);
        Nodes nodes = doc.query(xpath);
        int size = nodes.size();
        for ( int i = 0; i < size; i++ )
            System.out.println(xpc.calculateXPath(nodes.get(i)));
    }
}

我用于测试的文档:

<Urmel>
  <!-- spukt im Schloss -->
  <Monster xmlns="urn:X-Monster">
    <Gurke>
      <?Garten eins="zwei" drei="vier"?>
      <Heini Hecht="toll">
        <eins>eins</eins>
        <zwei id="ich-bin-die-zwei">zwei</zwei>
        <drei letzt="1">drei</drei>
      </Heini>
      <!-- Es kann nur einen geben :-) -->
    </Gurke>
    <Tomate id="pomodoro">
      <eene/>
      <meene/>
      <miste>Auweia!</miste>
      <aa>
        <bb>
          <cc>dd</cc>
        </bb>
      </aa>
    </Tomate>
  </Monster>
</Urmel>

远非完美,但我希望这会有所帮助! :-)

【讨论】:

  • 我创建了一个更短的版本,但它不能 100% 工作。我已经更新了我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-26
  • 1970-01-01
相关资源
最近更新 更多