XML文档的索引路径

时间:2015-08-31 22:26:53

标签: java xpath conceptual

我有一个索引Xpath的算法,这样我就可以通过为xpath节点分配唯一索引来处理具有相同Xpath但不同值的多个文件。

示例:

文件1:

<Return>
  <ReturnData>
    <Person>
      <Name>Yohanna</Name>
    </Person>
  </ReturnData>
</Return>

文件2:

<Return>
  <ReturnData>
    <Person>
      <Name>Jacoub</Name>
    </Person>
  </ReturnData>
</Return>

期望的输出:

1. /Return/ReturnData/Person[1]/Name=Yohanna
2. /Return/ReturnData/Person[2]/Name=Jacoub

我当前的实现为我提供了输出:

1. /Return/ReturnData[1]/Person[1]/Name[1]=Yohanna
2. /Return/ReturnData[1]/Person[2]/Name[1]=Jacoub

我想删除[1],因为它们不需要表示此节点只出现1次。

索引代码:

public String getFullXPath(Node n) {
    if (null == n) return null;

    Node parent;
    Stack<Node> hierarchy = new Stack<Node>();
    StringBuilder builder = new StringBuilder();

    hierarchy.push(n);

    switch (n.getNodeType()) {
        case Node.ATTRIBUTE_NODE:
            parent = ((Attr) n).getOwnerElement();
            break;
        case Node.ELEMENT_NODE:
            parent = n.getParentNode();
            break;
        case Node.DOCUMENT_NODE:
            parent = n.getParentNode();
            break;
        default:
            throw new IllegalStateException("Unexpected Node type" + n.getNodeType());
    }

    while (null != parent
            && parent.getNodeType() != Node.DOCUMENT_NODE
            && !parent.getNodeName().equals("section")) {

        hierarchy.push(parent);
        parent = parent.getParentNode();
    }

    Object obj;
    while (!hierarchy.isEmpty() && null != (obj = hierarchy.pop())) {
        Node node = (Node) obj;

        if (node.getNodeType() == Node.ELEMENT_NODE) { 
            builder.append("/").append(node.getNodeName());

            int prev_siblings = 1;
            Node prev_sibling = node.getPreviousSibling();
            while (null != prev_sibling) {
                if (prev_sibling.getNodeType() == node.getNodeType()) {
                    if (prev_sibling.getNodeName().equalsIgnoreCase(node.getNodeName())) {
                        prev_siblings++;
                    }
                }
                prev_sibling = prev_sibling.getPreviousSibling();
            }
            // Here is where I say don't append the number of prev_siblings if it equals 1 or the next sibling does not exist
            if(prev_siblings == 1 && node.getNextSibling() == null) {
            } 
            else 
                builder.append("[").append(prev_siblings).append("]");
        } 

        else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
            builder.append("/@");
            builder.append(node.getNodeName());
        }
    }

    return builder.toString();
}

我已经尝试解决这个问题,但经过3天的调查和调试后我仍然无法进行调试......不知道......我知道我错过了什么,我不知道看到。任何帮助或帮助将不胜感激。

编辑:

添加了2个辅助方法:

private static boolean hasNextElementsWithName(Node node) {
    while (null != node) {
        // checks if next sibling exists
        if(node.getNextSibling().hasAttributes()) {
            return true;
        }
    }
    return false;
}


private static int countPrevElementsWithName(Node node, int prev_siblings,
        Node prev_sibling) {
    while (null != prev_sibling) {

        if (prev_sibling.getNodeType() == node.getNodeType()) {
            if (prev_sibling.getNodeName().equalsIgnoreCase(node.getNodeName())) { 
                prev_siblings++;
            }
        }
        prev_sibling = prev_sibling.getPreviousSibling();

    }
    return prev_siblings;
}

通话方式:

    Object obj;
    while (!hierarchy.isEmpty() && null != (obj = hierarchy.pop())) {
        Node node = (Node) obj;

        if (node.getNodeType() == Node.ELEMENT_NODE) { 
            builder.append("/").append(node.getNodeName());

            int prev_siblings = 1;
            Node prev_sibling = node.getPreviousSibling();

            prev_siblings = countPrevElementsWithName(node, prev_siblings,
                    prev_sibling);


             //@Andreas
            int count = countPrevElementsWithName(node, prev_siblings, prev_sibling);
            if(count != 0 || hasNextElementsWithName(node)) {
                builder.append("[").append(count+1).append("]");
            }

        } 

        else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
            builder.append("/@");
            builder.append(node.getNodeName());
        }
    }

我现在不确定如何使用它们?

2 个答案:

答案 0 :(得分:2)

代码存根

int count = countPrevElementsWithSameName(node);
if (count != 0 || hasNextElementWithSameName(node))
    builder.append("[").append(count + 1).append("]");

助手方法

private static final boolean hasNextElementWithSameName(Node node) {
    String name = node.getNodeName();
    for (Node next = node.getNextSibling(); next != null; next = next.getNextSibling())
        if (next.getNodeType() == Node.ELEMENT_NODE) // only look at elements
            return next.getNodeName().equals(name); // stop on first element after "node"
    return false;
}
private static final int countPrevElementsWithSameName(Node node) {
    String name = node.getNodeName();
    int count = 0;
    for (Node prev = node.getPreviousSibling(); prev != null; prev = prev.getPreviousSibling())
        if (prev.getNodeType() == Node.ELEMENT_NODE) { // only look at elements
            if (! prev.getNodeName().equals(name))
                break; // stop when element name changes
            count++; // count elements of same name as "node"
        }
    return count;
}

答案 1 :(得分:1)

在Java / DOM中执行此操作非常痛苦,因此这是一个XSLT解决方案:

<xsl:template match="/" mode="path">
  <xsl:text>/</xsl:text>
</xsl:template>

<xsl:template match="*" mode="path">
  <xsl:apply-templates select=".." mode="path"/>
  <xsl:text>/</xsl:text>
  <xsl:value-of select="name()"/>
  <xsl:text>[</xsl:text>
  <xsl:number/>
  <xsl:text>]</xsl:text>
</xsl:template>

<xsl:template match="*[count(../*)=1]" mode="path">
  <xsl:apply-templates select=".." mode="path"/>
  <xsl:text>/</xsl:text>
  <xsl:value-of select="name()"/>
</xsl:template>

<xsl:template match="/">
  <xsl:for-each select="//Name">
    <xsl:apply-templates select="." mode="path"/>
    <xsl:text>=</xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>&#xa;</xsl:text>
  </xsl:for-each>
</xsl:template>

<xsl:strip-space elements="*"/>
<xsl:output method="text"/>