【问题标题】:Flatten XML using multiple XPath expressions使用多个 XPath 表达式展平 XML
【发布时间】:2009-12-26 18:02:06
【问题描述】:

我正在寻找一种通用算法,它可以将 XML 文件扁平化为表格,给定多个 XPath 表达式,并且由于可用 XPath 引擎实现的性质,我尝试过的所有事情都失败了。

给定一个 XML:

<A Name="NameA">
<B Name="NameB1">
    <C Name="NameC1"/>
    <C Name="NameC2"/>
    <C Name="NameC3"/>
</B>
<B Name="NameB2">
    <C Name="NameC4"/>
    <C Name="NameC5"/>
    <C Name="NameC6"/>
</B>

以及以下 XPath 表达式作为输入:

/A/@Name
/A/B/@Name
/A/B/C/@Name

输出应该是如下形式的表格:

NameA NameB1 NameC1

NameA NameB1 NameC2

NameA NameB1 NameC3

NameA NameB2 NameC4

名称A 名称B2 名称C5

名称A 名称B2 名称C6

我正在尝试使用可用的 Java XML 包(如 javax.xml.xpath、jdom 等)访问此表。但无济于事。

好像是

XPath.evaluate("/A/B/C/@Name", doc, XPathConstants.NODESET);

代码将返回一个无法遍历的“分离”节点。

我在 XPath 评估节点上尝试了多种递归方式,但均无济于事。还考虑了 DOM 树的 DFS 遍历,但所有 XPath 评估器似乎都返回分离节点,其中 node.getParent() 将始终返回“null”。

对于可以跟踪嵌套 XPath 表达式的“多 XPath 表达式感知”算法有什么想法吗?

我觉得使用 XSLT 很容易做到这一点,但我的 XSLT 技能相当生疏......

【问题讨论】:

  • 忘了说 XML 和 XPath 都是静态的。该表达式是在没有事先了解 XML 的情况下给出的。

标签: java xml xslt xpath


【解决方案1】:

这个 XSLT:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output indent="yes" />

    <xsl:template match="/">
    <table>
<!--Based upon your comments, it sounds as if you don't know what the structure of the XML you will be dealing with is(element nesting or attribute names).
        That makes it a little bit difficult.    
        Based upon the example XML you gave the following for-each will work:-->
        <xsl:for-each select="//C"> <!--You could also use "/A/B/C" -->
        <tr>
<!--This looks up the node tree and creates a column for the current element, as well as for each of it's parents, using the first Attribute as the value.-->
            <xsl:for-each select="ancestor-or-self::*">
            <td><xsl:value-of select="@*[1]"/></td>
            </xsl:for-each>
        </tr>
        </xsl:for-each>
    </table>
    </xsl:template>

</xsl:stylesheet>

适用于提供的 XML 并生成以下内容:

<?xml version="1.0" encoding="UTF-16"?>
<table>
<tr>
<td>NameA</td>
<td>NameB1</td>
<td>NameC1</td>
</tr>
<tr>
<td>NameA</td>
<td>NameB1</td>
<td>NameC2</td>
</tr>
<tr>
<td>NameA</td>
<td>NameB1</td>
<td>NameC3</td>
</tr>
<tr>
<td>NameA</td>
<td>NameB2</td>
<td>NameC4</td>
</tr>
<tr>
<td>NameA</td>
<td>NameB2</td>
<td>NameC5</td>
</tr>
<tr>
<td>NameA</td>
<td>NameB2</td>
<td>NameC6</td>
</tr>
</table>

【讨论】:

    【解决方案2】:

    我希望您可以使用 XSLT2 来做到这一点。 (如果您仅限于 XSLT1,那么我不确定)。 有关教程,请参阅 http://www.xml.com/pub/a/2003/11/05/tr.html。您可以有多个 group-by 指令,它们都采用 XPath。我无法立即为您的问题提供代码,但如果您阅读教程,我认为它映射得很好。

    【讨论】:

      【解决方案3】:

      编辑同样的事情,但使用 XPath:

              XPathFactory f = XPathFactory.newInstance();
              XPath xPath = f.newXPath();
              NodeList list = (NodeList) xPath.evaluate("//*[* and not(*/*)]/*", new InputSource(stream), XPathConstants.NODESET);
      
              for (int i = 0; i < list.getLength(); i++) {
                  Node n = list.item(i);
                  Stack<Node> s = new Stack<Node>();
      
                  while (n != null) {
                      s.push(n);
                      n = n.getParentNode();
                  }
      
                  s.pop(); //this is document root, we don't need it
      
                  while (s.size() > 0) {
                      NamedNodeMap map = s.pop().getAttributes();
      
                      for (int j = 0; j < map.getLength(); j++) {
                          Node node = map.item(j);
                          System.out.print(node.getNodeName() + ": " + node.getTextContent() + " ");
                      }
                  }
      
                  System.out.println("");
              } 
      

      您可以使用常规的 DOM 函数。它不如 XPath 好,但通用,适用于任何 XML 文件。

      如果我理解正确,那么这段代码就可以解决问题:

          String xml = "<A Name=\"NameA\">\n" +
                  "<B Name=\"NameB1\">\n" +
                  "        <C Name=\"NameC1\"> </C>\n" +
                  "        <C Name=\"NameC2\"/>\n" +
                  "        <C Name=\"NameC3\"/>\n" +
                  "</B>\n" +
                  "<B Name=\"NameB2\">\n" +
                  "        <C Name=\"NameC4\"/>\n" +
                  "        <C Name=\"NameC5\"/>\n" +
                  "        <C Name=\"NameC6\"/>\n" +
                  "</B></A>";
          try {
              DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
              Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes()));
      
              Queue<Node> q = new LinkedList<Node>();
      
              q.add(doc.getFirstChild());
              //start BFS
              while (q.size() > 0) {
                  Node n = q.poll();
                  NodeList childNodes = n.getChildNodes();
                  //add all children of current node
                  int elemNodes = 0;
                  for (int i = 0; i < childNodes.getLength(); i++) {
                      Node node = childNodes.item(i);
                      if (node.getNodeType() == Node.ELEMENT_NODE) {
                          elemNodes++;
                          q.add(node);
                      }
                  }
                  //if node has no children, print its path
                  if (elemNodes == 0) {
                      Stack<Node> s = new Stack<Node>();
      
                      while (n != null) {
                          s.push(n);
                          n = n.getParentNode();
                      }
      
                      s.pop(); //this is document root, we don't need it
      
                      while (s.size() > 0)
                          System.out.print(s.pop().getAttributes().getNamedItem("Name").getTextContent() + " ");
      
                      System.out.println("");
                  }
              }
          } catch (ParserConfigurationException e) {
              e.printStackTrace();
          } catch (SAXException e) {
              e.printStackTrace();
          } catch (IOException e) {
              e.printStackTrace();
          }
      

      【讨论】:

      • 伟大的回应朝圣者!这是简单遍历所有 DOM 树的好方法 :) 但我的问题是我要处理的 XML 可能有如下表达式: /A/@Name /A/B/@AnotherName /A/B/C /D/E/@ADifferentName 所以“名称”常量不起作用:(
      • 名称不是常量,可以获取节点的任意属性。
      猜你喜欢
      • 2012-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-21
      相关资源
      最近更新 更多