问题
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>
</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