webentwicklung-frage-antwort-db.com.de

Hübsches Druck-XML in Java 8

Ich habe eine XML-Datei als DOM-Dokument gespeichert und möchte sie auf der Konsole drucken, vorzugsweise ohne eine externe Bibliothek. Mir ist bekannt, dass diese Frage auf dieser Website mehrmals gestellt wurde, jedoch hat keine der vorherigen Antworten für mich funktioniert. Ich benutze Java 8, also unterscheidet sich mein Code vielleicht von den vorherigen Fragen? Ich habe auch versucht, den Transformator manuell mithilfe von im Web gefundenem Code einzustellen. Dies hat jedoch nur einen not found-Fehler verursacht.

Hier ist mein Code, der gerade jedes xml-Element in einer neuen Zeile links von 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();
    }

}
21
Hungry

Ich schätze, dass das Problem in der Originaldatei mit leeren Textknoten zusammenhängt (d. H. Textknoten mit nur Leerzeichen). Sie sollten versuchen, sie mit dem folgenden Code unmittelbar nach der Analyse programmgesteuert zu entfernen. Wenn Sie sie nicht entfernen, werden sie durch die Transformer beibehalten.

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));
}
9
Aldo

Als Antwort auf Espinosas Kommentar gibt es hier eine Lösung, wenn "das Original-XML ist nicht bereits (teilweise) eingerückt oder enthält neue Zeilen".

Hintergrund

Auszug aus dem Artikel (siehe Referenzen unten) zur Lösung dieser Lösung:

Basierend auf der DOM-Spezifikation sind Whitespaces außerhalb der Tags vollkommen gültig und werden ordnungsgemäß beibehalten. Wenn Sie sie entfernen möchten, können Sie mit dem Normalize-Space von XPath alle Whitespace-Knoten lokalisieren und zuerst 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>

Verweise

41
Stephan

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);
}
3
Tom

Ich habe eine simple-Klasse zum Entfernen von Leerzeichen in Dokumenten geschrieben - unterstützt Befehlszeilen und verwendet DOM/XPath nicht.

Edit: Wenn man bedenkt, das Projekt enthält auch einen hübschen Drucker, der vorhandene Leerzeichen behandelt:

PrettyPrinter prettyPrinter = PrettyPrinterBuilder.newPrettyPrinter().ignoreWhitespace().build();
2
ThomasRS

Ich mochte keine der gängigen XML-Formatierungslösungen, da sie alle mehr als 1 aufeinander folgende neue Zeilenzeichen entfernen (aus irgendeinem Grund sind Leerzeichen/Tabulatoren und neue Zeilenzeichen untrennbar ...). Hier ist meine Lösung, die eigentlich für XHTML entwickelt wurde, aber auch die Arbeit mit XML erledigen sollte:

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 gilt eine Annahme : Es gibt keine <> -Zeichen außer denen, die die XML/XHTML-Tags enthalten.

0
Andrew

Underscore-Java hat die statische Methode U.formatXml (Zeichenfolge). Ich bin der Betreuer des Projekts. Live-Beispiel

import com.github.underscore.lodash.U;

public class MyClass {
    public static void main(String args[]) {
        String xml = "<root>" + //
             "\n   "  + //
             "\n<name>Coco Puff</name>" + //
             "\n        <total>10</total>    </root>";

        System.out.println(U.formatXml(xml));
    }
}

Ausgabe:

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