Java: получить xpath элемента в документе org.w3c.dom - PullRequest
7 голосов
/ 11 марта 2011

Я написал то, чего хочу достичь. однако функция 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;
    }

Ответы [ 3 ]

6 голосов
/ 23 марта 2011

Ваш заголовок говорит о 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.

4 голосов
/ 11 марта 2011

Поддержка Dom4j xpath действительно хороша, вы можете получить доступ к любому элементу, предоставив выражение xpath.
Однако я не уверен, верно ли обратное, т. Е. При наличии элемента вы можете получить выражение xpath.

См. API на http://www.docjar.com/projects/dom4j-1.6.1-code.html

Обратите внимание, что избегайте www.dom4j.org, похоже, он был обманут какой-то фермой спам-ссылок.

1 голос
/ 23 марта 2011

Я играл с библиотекой XOM , которая имеет хороший 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>

Далеко не идеально, но я надеюсь, что это поможет!: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...