2014-09-16 14 views
18

Ich habe eine XML-Datei als DOM-Dokument gespeichert und ich würde es gerne auf der Konsole, vorzugsweise ohne eine externe Bibliothek zu drucken. Ich bin mir bewusst, dass diese Frage mehrfach auf dieser Seite gestellt wurde, aber keine der vorherigen Antworten hat für mich funktioniert. Ich benutze Java 8, also unterscheidet sich mein Code vielleicht von vorherigen Fragen? Ich habe auch versucht, den Transformator manuell mit Code aus dem Internet gefunden, aber dies verursacht nur einen not found Fehler.Pretty print XML in Java 8

Hier ist mein Code, der gerade jedes xml Element in einer neuen Zeile auf der linken Seite der Konsole ausgibt.

import java.io.*; 
import javax.xml.parsers.*; 
import javax.xml.transform.*; 
import javax.xml.transform.dom.DOMSource; 
import javax.xml.transform.stream.StreamResult; 

import org.w3c.dom.Document; 
import org.xml.sax.InputSource; 
import org.xml.sax.SAXException; 


public class Test { 
    public Test(){ 
     try { 
      //java.lang.System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.xsltc.trax.TransformerFactoryImpl"); 

      DocumentBuilderFactory dbFactory; 
      DocumentBuilder dBuilder; 
      Document original = null; 
      try { 
       dbFactory = DocumentBuilderFactory.newInstance(); 
       dBuilder = dbFactory.newDocumentBuilder(); 
       original = dBuilder.parse(new InputSource(new InputStreamReader(new FileInputStream("xml Store - Copy.xml")))); 
      } catch (SAXException | IOException | ParserConfigurationException e) { 
       e.printStackTrace(); 
      } 
      StringWriter stringWriter = new StringWriter(); 
      StreamResult xmlOutput = new StreamResult(stringWriter); 
      TransformerFactory tf = TransformerFactory.newInstance(); 
      //tf.setAttribute("indent-number", 2); 
      Transformer transformer = tf.newTransformer(); 
      transformer.setOutputProperty(OutputKeys.METHOD, "xml"); 
      transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 
      transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); 
      transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 
      transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 
      transformer.transform(new DOMSource(original), xmlOutput); 
      java.lang.System.out.println(xmlOutput.getWriter().toString()); 
     } catch (Exception ex) { 
      throw new RuntimeException("Error converting to String", ex); 
     } 
    } 

    public static void main(String[] args){ 
     new Test(); 
    } 

} 

Antwort

9

Ich denke, dass das Problem zu leeren Textknoten verbunden ist (das heißt Textknoten mit nur Leerzeichen) in der Originaldatei. Sie sollten versuchen, sie direkt nach dem Parsing mithilfe des folgenden Codes programmgesteuert zu entfernen. Wenn Sie sie nicht entfernen, wird die Transformer sie erhalten.

original.getDocumentElement().normalize(); 
XPathExpression xpath = XPathFactory.newInstance().newXPath().compile("//text()[normalize-space(.) = '']"); 
NodeList blankTextNodes = (NodeList) xpath.evaluate(original, XPathConstants.NODESET); 

for (int i = 0; i < blankTextNodes.getLength(); i++) { 
    blankTextNodes.item(i).getParentNode().removeChild(blankTextNodes.item(i)); 
} 
+0

Das ist perfekt! Prost. – Hungry

-2

erstellen XML-Datei:

new FileInputStream("xml Store - Copy.xml") ;// result xml file format incorrect ! 

, so dass, wenn der Inhalt der Eingabe gegeben analysiert Quelle als XML-Dokument und gibt ein neues DOM-Objekt.

Document original = null; 
... 
original.parse("data.xml");//input source as an XML document 
3

Dies funktioniert auf Java 8:

public static void main (String[] args) throws Exception { 
    String xmlString = "<hello><from>ME</from></hello>"; 
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 
    DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); 
    Document document = documentBuilder.parse(new InputSource(new StringReader(xmlString))); 
    pretty(document, System.out, 2); 
} 

private static void pretty(Document document, OutputStream outputStream, int indent) throws Exception { 
    TransformerFactory transformerFactory = TransformerFactory.newInstance(); 
    Transformer transformer = transformerFactory.newTransformer(); 
    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 
    if (indent > 0) { 
     transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 
     transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", Integer.toString(indent)); 
    } 
    Result result = new StreamResult(outputStream); 
    Source source = new DOMSource(document); 
    transformer.transform(source, result); 
} 
+0

Hmmm, das funktioniert auch für mich, also denke ich, das Problem muss in der Art sein, wie ich die XML-Datei lese. – Hungry

+4

Warnung, diese Lösung funktioniert nur, wenn in der ursprünglichen XML nicht bereits (teilweise) eingerückt ist oder neue Zeilen enthalten. Das heißt, es wird funktionieren für " ME", aber nicht für " \ n ME \ n" – Espinosa

+1

zu lässig Leser, hier ist eine Lösung für @ Espinosa Warnung: http://stackoverflow.com/ a/33541820/363573 – Stephan

2

ich simple class für zum Entfernen von Leerzeichen in den Dokumenten geschrieben haben - unterstützt Befehlszeile und nicht DOM/XPath verwenden.

Edit: Kommen Sie, daran zu denken, enthält das Projekt auch einen recht-Drucker, die vorhandenen Leerzeichen behandelt:

PrettyPrinter prettyPrinter = PrettyPrinterBuilder.newPrettyPrinter().ignoreWhitespace().build(); 
35

In Antwort auf Espinosa Kommentar, hier ist eine Lösung, wenn "die ursprüngliche xml nicht ist bereits (teilweise) eingerückt oder neue Zeilen enthalten ".

Hintergrund

Auszug aus dem Artikel (Referenzen siehe unten) inspiriert diese Lösung:

Basierend auf der DOM-Spezifikation, Whitespaces außerhalb der Tags perfekt gültig ist, und sie sind richtig konserviert. Um sie zu entfernen, können wir den Normalisierungsraum von XPath verwenden, um alle Leerzeichen zu lokalisieren und sie zuerst zu entfernen.

Java-Code

public static String toPrettyString(String xml, int indent) { 
    try { 
     // Turn xml string into a document 
     Document document = DocumentBuilderFactory.newInstance() 
       .newDocumentBuilder() 
       .parse(new InputSource(new ByteArrayInputStream(xml.getBytes("utf-8")))); 

     // Remove whitespaces outside tags 
     document.normalize(); 
     XPath xPath = XPathFactory.newInstance().newXPath(); 
     NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']", 
                 document, 
                 XPathConstants.NODESET); 

     for (int i = 0; i < nodeList.getLength(); ++i) { 
      Node node = nodeList.item(i); 
      node.getParentNode().removeChild(node); 
     } 

     // Setup pretty print options 
     TransformerFactory transformerFactory = TransformerFactory.newInstance(); 
     transformerFactory.setAttribute("indent-number", indent); 
     Transformer transformer = transformerFactory.newTransformer(); 
     transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 
     transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 
     transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 

     // Return pretty print xml string 
     StringWriter stringWriter = new StringWriter(); 
     transformer.transform(new DOMSource(document), new StreamResult(stringWriter)); 
     return stringWriter.toString(); 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

Verwendungsbeispiel

String xml = "<root>" + // 
      "\n " + // 
      "\n<name>Coco Puff</name>" + // 
      "\n  <total>10</total> </root>"; 

System.out.println(toPrettyString(xml, 4)); 

Ausgabe

<root> 
    <name>Coco Puff</name> 
    <total>10</total> 
</root> 

Referenzen

+0

Das ist eigentlich ziemlich ähnlich dem Code, den ich am Ende benutzt habe :). – Hungry

+1

@ btrs20 Der Unterschied beruht auf der Entfernung von Leerzeichen. – Stephan

+1

Ich landete etwas ähnliches, einfache Rekursion auf der Suche nach Leerzeichen nur Textknoten, keine XPath. Dein Code ist kürzer. Schönes Beispiel für fortschrittlichen XPath. Vielen Dank. – Espinosa

0
veröffentlicht

ich nicht der gemeinsamen XML-Formatierungs Lösungen gefallen hat, weil sie alle mehr als 1 Mal in Folge neue Zeile entfernen Zeichen (aus irgendeinem Grund, Leerzeichen/Tabs entfernen und neue Zeile Zeichen entfernen sind untrennbar ...). Hier ist meine Lösung, die für XHTML tatsächlich wurde aber sollte die Arbeit mit XML auch tun:

public String GenerateTabs(int tabLevel) { 
    char[] tabs = new char[tabLevel * 2]; 
    Arrays.fill(tabs, ' '); 

    //Or: 
    //char[] tabs = new char[tabLevel]; 
    //Arrays.fill(tabs, '\t'); 

    return new String(tabs); 
} 

public String FormatXHTMLCode(String code) { 
    // Split on new lines. 
    String[] splitLines = code.split("\\n", 0); 

    int tabLevel = 0; 

    // Go through each line. 
    for (int lineNum = 0; lineNum < splitLines.length; ++lineNum) { 
    String currentLine = splitLines[lineNum]; 

    if (currentLine.trim().isEmpty()) { 
     splitLines[lineNum] = ""; 
    } else if (currentLine.matches(".*<[^/!][^<>]+?(?<!/)>?")) { 
     splitLines[lineNum] = GenerateTabs(tabLevel) + splitLines[lineNum]; 

     ++tabLevel; 
    } else if (currentLine.matches(".*</[^<>]+?>")) { 
     --tabLevel; 

     if (tabLevel < 0) { 
     tabLevel = 0; 
     } 

     splitLines[lineNum] = GenerateTabs(tabLevel) + splitLines[lineNum]; 
    } else if (currentLine.matches("[^<>]*?/>")) { 
     splitLines[lineNum] = GenerateTabs(tabLevel) + splitLines[lineNum]; 

     --tabLevel; 

     if (tabLevel < 0) { 
     tabLevel = 0; 
     } 
    } else { 
     splitLines[lineNum] = GenerateTabs(tabLevel) + splitLines[lineNum]; 
    } 
    } 

    return String.join("\n", splitLines); 
} 

Es macht eine Annahme: dass es keine <> Zeichen außer für diejenigen, die die XML/XHTML umfassen Stichworte.

+1

Dieses Snippet ist unvollständig, da die Variable codeGenerator nicht aufgelöst werden kann. ist die entsprechende Klasse in Java geschrieben? da Java-Methodennamen eine andere Namenskonvention haben. – benez

+0

@benez Sorry, und danke für die Information. Ich wusste nicht, dass externer Code verwendet wurde. Versuchen Sie das, ich denke, es wird funktionieren; kann es jetzt nicht testen. – Andrew