Index path for XML document

心已入冬 提交于 2019-12-12 03:58:37

问题


I've got an algorithm for indexing an Xpath so that I can process multiple files with the same Xpath but different values via assigning an xpath node a unique index.

Example:

File 1:

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

File 2:

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

Desired output:

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

My current implementation gives me an output of:

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

I'd like to remove the [1]'s since they are not needed to signify there is only 1 occurrence of this node.

Indexing code:

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();
}

I've tried fixing this but I am still unable after 3 days of looking into this and debugging...No idea...I know I'm missing something, something I'm not seeing. Any help or assistance would be much appreciated.

Edit:

Added 2 helper methods:

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;
}

Calling Method:

    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());
        }
    }

I'm not sure how to use them now?


回答1:


Code stub

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

Helper methods

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;
}



回答2:


Doing this in Java/DOM is just painful, so here is an XSLT solution:

<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"/>


来源:https://stackoverflow.com/questions/32320867/index-path-for-xml-document

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!