2009-12-26 3 views
2

Я ищу общий алгоритм, который может сгладить XML-файл в таблицу, учитывая несколько выражений XPath и все, что я пробовал, из-за природы доступных версий XPath.Сглаживание XML с использованием нескольких выражений 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 

Вывод должен быть таблица в следующем виде:

НАМЕА NameB1 NameC1

НАМЕА NameB1 NameC2

НАМЕА NameB1 NameC3

НАМЕА NameB2 NameC4

НАМЕА NameB2 NameC5

НАМЕА NameB2 NameC6

Я пытаюсь получить в эту таблицу с имеющимися Java XML пакеты, такие как javax.xml.xpath, jdom и т. д., безрезультатно.

Похоже, что код

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

возвратит «обособленный» узел, который не может быть пройден.

Я пробовал много способов рекурсии на оцененных по XPath узлах безрезультатно. Также подумал об обходе DFS дерева DOM, но снова все оценщики XPath, похоже, возвращают отдельные узлы, где node.getParent() всегда возвращает «null».

Любые идеи для алгоритма с поддержкой мульти-XPath-выражений, который может отслеживать вложенные выражения XPath?

У меня есть чувство, что это можно легко с помощью XSLT, но мои навыки XSLT довольно ржавый ...

+0

Забыл упомянуть, что XML, ни XPath являются статическими. Выражение дано без предварительного знания XML. – yarinbenado

ответ

0

EDIT То же самое, но с 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(); 
    } 
+0

Отличный ответ Piligrim! Это хороший подход к простому траверсу всего DOM дерево :) Но моя проблема в том, что XML я бы иметь дело с, может иметь такие выражения, как: /A/@ Имя /A/B/@ AnotherName /A/B/C/D/E/@ ADifferentName поэтому константа «Имя» не будет работать :( – yarinbenado

+0

Имя не является константой, вы можете получить любой атрибут узла. –

+0

ok, я изменил код для обработки любого атрибута. –

0

Я ожидаю, что вы могли бы сделать это с XSLT2. (Если вы ограничены XSLT1, то я не уверен). См. http://www.xml.com/pub/a/2003/11/05/tr.html для учебника. У вас может быть несколько групповых инструкций, и все они берут XPath. Я не могу сразу дать вам код для вашей проблемы, но если вы прочтете учебник, я думаю, что он хорошо отображает.

3

Это 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> 
Смежные вопросы