如何使用java为XML中的节点生成Xpath?

时间:2022-02-03 01:34:42

I have a piece of code to generate xpath for a node. But it doesn't create array structure of it. For example, if an element has two elements with same name, I need to provide index to point to them appropriately. An illustration of it is below.

我有一段代码为节点生成xpath。但它不会创建它的数组结构。例如,如果一个元素有两个具有相同名称的元素,我需要提供索引以适当地指向它们。它的一个例子在下面。

<abc>
  <def>
     </hij>
  </def>
  <def>
     </lmn>
  </def>
</abc>

Now, to get xpath for hij , I would need something like this:

现在,为了得到hij的xpath,我需要这样的东西:

//abc[1]/def[1]/hij

To get xpath for lmn , I would need something like this:

要获得lmn的xpath,我需要这样的东西:

//abc[1]/def[2]/lmn

I have a piece of code which will simply give me //abc/def/hij and //abc/def/lmn

我有一段代码只会给我// abc / def / hij和// abc / def / lmn

private String getXPath(Node root, String elementName)
    {
        for (int i = 0; i < root.getChildNodes().getLength(); i++)
        {
            Node node = root.getChildNodes().item(i);
            if (node instanceof Element)
            {
                if (node.getNodeName().equals(elementName))
                {
                    return "\\" + node.getNodeName();
                }
                else if (node.getChildNodes().getLength() > 0)
                {
                    if(map.containsKey(node.getNodeName()))
                        map.put(node.getNodeName(), map.get(node.getNodeName())+1);
                    else
                        map.put(node.getNodeName(), 1);

                    this.xpath = getXPath(node, elementName);
                    if (this.xpath != null){
                        return "\\" + node.getNodeName() +"["+map.get(node.getNodeName())+"]"+ this.xpath;
                    }
                }
            }
        }

        return null;
    }

Can someone help me to append array structure with this?

有人可以帮我添加数组结构吗?

2 个解决方案

#1


0  

<abc>
  <def>
    </hij>
  </def>
  <def>
    </lmn>
  </def>
</abc>

In here. You are closing

在这里。你正在结束

</hij> 

and

</lmn>

without opening them. If you are opening them before abc, them you can't close them inside abc. Basically: You can't intertwince them. Open one, open second, close second, close one. Never in another way around.

没有打开它们。如果你在abc之前打开它们,你就无法在abc中关闭它们。基本上:你不能交织他们。打开一个,打开第二个,关闭第二个,关闭一个。从来没有以另一种方式。

This might lead to your error

这可能会导致您的错误

#2


0  

I'm unable to fix your code in the question as it is not complete, for example where is map defined? Also see other answer about your malformed input.

我无法在问题中修复您的代码,因为它不完整,例如地图定义在哪里?另请参阅有关您的格式错误输入的其他答案。

Making the assumption that hij and lmn should have been short tags, here is a complete solution.

假设hij和lmn应该是短标签,这是一个完整的解决方案。

  • I used the approach of navigating up the tree using getParentNode().
  • 我使用了使用getParentNode()导航树的方法。

  • I've included XPath tests to check that the generated expression returns the same node
  • 我已经包含XPath测试来检查生成的表达式是否返回相同的节点

  • Extended input to contain elements of different names at the same level.
  • 扩展输入以包含同一级别的不同名称的元素。

Code

public class Test {

    private static String getXPath(Node root) {
        Node current = root;
        String output = "";
        while (current.getParentNode() != null) {
            Node parent = current.getParentNode();
            if (parent != null && parent.getChildNodes().getLength() > 1) {
                int nthChild = 1;
                Node siblingSearch = current;
                while ((siblingSearch = siblingSearch.getPreviousSibling()) != null) {
                    // only count siblings of same type
                    if (siblingSearch.getNodeName().equals(current.getNodeName())) {
                        nthChild++;
                    }
                }
                output = "/" + current.getNodeName() + "[" + nthChild + "]" + output;
            } else {
                output = "/" + current.getNodeName() + output;
            }
            current = current.getParentNode();
        }
        return output;
    }

    public static void main(String[] args) throws Exception {

        String input = "<abc><def><hij /></def><def><lmn /><xyz /><lmn /></def></abc>";
        Document root = DocumentBuilderFactory.newInstance()
                .newDocumentBuilder()
                .parse(new InputSource(new StringReader(input)));

        test(root.getDocumentElement(), root);
    }

    private static void test(Node node, Document doc) throws Exception {
        String expression = getXPath(node);
        Node result = (Node) XPathFactory.newInstance().newXPath()
                .compile(expression).evaluate(doc, XPathConstants.NODE);
        if (result == node) {
            System.out.println("Test OK  : " + expression);
        } else {
            System.out.println("Test Fail: " + expression);
        }
        for (int i = 0; i < node.getChildNodes().getLength(); i++) {
            test(node.getChildNodes().item(i), doc);
        }
    }
}

Output

Test OK  : /abc
Test OK  : /abc/def[1]
Test OK  : /abc/def[1]/hij
Test OK  : /abc/def[2]
Test OK  : /abc/def[2]/lmn[1]
Test OK  : /abc/def[2]/xyz[1]
Test OK  : /abc/def[2]/lmn[2]

#1


0  

<abc>
  <def>
    </hij>
  </def>
  <def>
    </lmn>
  </def>
</abc>

In here. You are closing

在这里。你正在结束

</hij> 

and

</lmn>

without opening them. If you are opening them before abc, them you can't close them inside abc. Basically: You can't intertwince them. Open one, open second, close second, close one. Never in another way around.

没有打开它们。如果你在abc之前打开它们,你就无法在abc中关闭它们。基本上:你不能交织他们。打开一个,打开第二个,关闭第二个,关闭一个。从来没有以另一种方式。

This might lead to your error

这可能会导致您的错误

#2


0  

I'm unable to fix your code in the question as it is not complete, for example where is map defined? Also see other answer about your malformed input.

我无法在问题中修复您的代码,因为它不完整,例如地图定义在哪里?另请参阅有关您的格式错误输入的其他答案。

Making the assumption that hij and lmn should have been short tags, here is a complete solution.

假设hij和lmn应该是短标签,这是一个完整的解决方案。

  • I used the approach of navigating up the tree using getParentNode().
  • 我使用了使用getParentNode()导航树的方法。

  • I've included XPath tests to check that the generated expression returns the same node
  • 我已经包含XPath测试来检查生成的表达式是否返回相同的节点

  • Extended input to contain elements of different names at the same level.
  • 扩展输入以包含同一级别的不同名称的元素。

Code

public class Test {

    private static String getXPath(Node root) {
        Node current = root;
        String output = "";
        while (current.getParentNode() != null) {
            Node parent = current.getParentNode();
            if (parent != null && parent.getChildNodes().getLength() > 1) {
                int nthChild = 1;
                Node siblingSearch = current;
                while ((siblingSearch = siblingSearch.getPreviousSibling()) != null) {
                    // only count siblings of same type
                    if (siblingSearch.getNodeName().equals(current.getNodeName())) {
                        nthChild++;
                    }
                }
                output = "/" + current.getNodeName() + "[" + nthChild + "]" + output;
            } else {
                output = "/" + current.getNodeName() + output;
            }
            current = current.getParentNode();
        }
        return output;
    }

    public static void main(String[] args) throws Exception {

        String input = "<abc><def><hij /></def><def><lmn /><xyz /><lmn /></def></abc>";
        Document root = DocumentBuilderFactory.newInstance()
                .newDocumentBuilder()
                .parse(new InputSource(new StringReader(input)));

        test(root.getDocumentElement(), root);
    }

    private static void test(Node node, Document doc) throws Exception {
        String expression = getXPath(node);
        Node result = (Node) XPathFactory.newInstance().newXPath()
                .compile(expression).evaluate(doc, XPathConstants.NODE);
        if (result == node) {
            System.out.println("Test OK  : " + expression);
        } else {
            System.out.println("Test Fail: " + expression);
        }
        for (int i = 0; i < node.getChildNodes().getLength(); i++) {
            test(node.getChildNodes().item(i), doc);
        }
    }
}

Output

Test OK  : /abc
Test OK  : /abc/def[1]
Test OK  : /abc/def[1]/hij
Test OK  : /abc/def[2]
Test OK  : /abc/def[2]/lmn[1]
Test OK  : /abc/def[2]/xyz[1]
Test OK  : /abc/def[2]/lmn[2]