Как красиво распечатать XML с Java?

У меня есть строка Java, содержащая XML, без перевода строки или отступов. Я хотел бы превратить его в строку с хорошо отформатированным XML. Как мне это сделать?

String unformattedXml = "<tag><nested>hello</nested></tag>";
String formattedXml = new [UnknownClass]().format(unformattedXml);

Примечание. Я ввел Нить. Мой результат - Нить.

(Базовый) фиктивный результат:

<?xml version = "1.0" encoding = "UTF-8"?>
<root>
  <tag>
    <nested>hello</nested>
  </tag>
</root>

проверьте этот вопрос: stackoverflow.com/questions/1264849/…

dfa 12.08.2009 12:14

Просто любопытно, вы отправляете этот вывод в файл XML или что-то еще, где отступы действительно имеют значение? Некоторое время назад я был очень обеспокоен форматированием своего XML, чтобы он правильно отображался ... но потратив на это кучу времени, я понял, что мне нужно отправить свой вывод в веб-браузер и любой относительно современный веб-браузер. фактически отобразит XML в красивой древовидной структуре, так что я могу забыть об этой проблеме и двигаться дальше. Я упоминаю об этом на тот случай, если вы (или другой пользователь с той же проблемой) могли упустить ту же деталь.

Abel Morelos 06.10.2010 21:21

@Abel, сохранение в текстовые файлы, вставка в текстовые поля HTML и выгрузка в консоль для целей отладки.

Steve McLeod 07.10.2010 00:48

"отложено как слишком широкое" - это трудно быть более точным, чем вопрос сейчас!

Steve McLeod 16.07.2018 14:19
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
460
4
477 763
34
Перейти к ответу Данный вопрос помечен как решенный

Ответы 34

Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
//initialize StreamResult with File object to save to file
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
String xmlString = result.getWriter().toString();
System.out.println(xmlString);

Примечание. Результаты могут отличаться в зависимости от версии Java. Найдите обходные пути, специфичные для вашей платформы.

Как сделать так, чтобы на выходе не было <?xml version = "1.0" encoding = "UTF-8"?>?

Thang Pham 19.07.2011 23:26

Чтобы опустить декларацию <?xml ...>, добавьте transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATIO‌​N, "yes")

rustyx 25.08.2015 23:01

Обычным читателям может быть полезна улучшенная версия решения, описанного здесь (stackoverflow.com/a/33541820/363573).

Stephan 05.11.2015 13:20

где определяется doc?

Florian F 26.10.2017 13:29

это переменная типа Document, которая содержит ваш xml для преобразования, у вас должен быть он где-то в вашем коде

Lorenzo Boccaccia 26.10.2017 16:17

Это не отвечает на мой вопрос: как отформатировать строку, содержащую XML? В этом ответе уже предполагается, что вы каким-то образом преобразовали объект String в другой объект.

Steve McLeod 11.07.2018 23:49

Это не сайт для домашних заданий / фриланса, и решение охватывает наиболее важный аспект проблемы.

Lorenzo Boccaccia 09.10.2019 19:18

Это решение удаляет обертки CDATA. Есть ли флаг, чтобы предотвратить это?

Marinos An 21.07.2020 17:03

doc можно получить так: DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();DocumentBuilder db = dbf.newDocumentBuilder();InputSource is = new InputSource(new StringReader(in));Document doc = db.parse(is);

PunchyRascal 18.09.2020 14:30

Поскольку вы начинаете с String, вам необходимо скрыться от объекта DOM (например, Node), прежде чем вы сможете использовать Transformer. Однако, если вы знаете, что ваша XML-строка действительна, и вы не хотите нести накладные расходы памяти на синтаксический анализ строки в DOM, а затем запускать преобразование через DOM для возврата строки - вы можете просто сделать несколько старомодных посимвольный разбор. Вставляйте новую строку и пробелы после каждого символа </...>, сохраняйте счетчик отступов (для определения количества пробелов), который вы увеличиваете для каждого <...> и уменьшаете для каждого </...>, который вы видите.

Заявление об отказе от ответственности - я вырезал / вставил / отредактировал следующие функции, поэтому они могут не компилироваться как есть.

public static final Element createDOM(String strXML) 
    throws ParserConfigurationException, SAXException, IOException {

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setValidating(true);
    DocumentBuilder db = dbf.newDocumentBuilder();
    InputSource sourceXML = new InputSource(new StringReader(strXML));
    Document xmlDoc = db.parse(sourceXML);
    Element e = xmlDoc.getDocumentElement();
    e.normalize();
    return e;
}

public static final void prettyPrint(Node xml, OutputStream out)
    throws TransformerConfigurationException, TransformerFactoryConfigurationError, TransformerException {
    Transformer tf = TransformerFactory.newInstance().newTransformer();
    tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    tf.setOutputProperty(OutputKeys.INDENT, "yes");
    tf.transform(new DOMSource(xml), new StreamResult(out));
}

«Однако, если вы знаете, что ваша строка XML действительна ...» - хороший аргумент. См. Мое решение, основанное на этом подходе ниже.

David Easley 27.05.2010 14:51

Вот ответ на мой вопрос. Я объединил ответы из различных результатов, чтобы написать класс, который хорошо печатает XML.

Никаких гарантий относительно того, как он ответит неверным XML или большими документами.

package ecb.sdw.pretty;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;

/**
 * Pretty-prints xml, supplied as a string.
 * <p/>
 * eg.
 * <code>
 * String formattedXml = new XmlFormatter().format("<tag><nested>hello</nested></tag>");
 * </code>
 */
public class XmlFormatter {

    public XmlFormatter() {
    }

    public String format(String unformattedXml) {
        try {
            final Document document = parseXmlFile(unformattedXml);

            OutputFormat format = new OutputFormat(document);
            format.setLineWidth(65);
            format.setIndenting(true);
            format.setIndent(2);
            Writer out = new StringWriter();
            XMLSerializer serializer = new XMLSerializer(out, format);
            serializer.serialize(document);

            return out.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private Document parseXmlFile(String in) {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            InputSource is = new InputSource(new StringReader(in));
            return db.parse(is);
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        String unformattedXml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?><QueryMessage\n" +
                        "        xmlns=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message\"\n" +
                        "        xmlns:query=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/query\">\n" +
                        "    <Query>\n" +
                        "        <query:CategorySchemeWhere>\n" +
                        "   \t\t\t\t\t         <query:AgencyID>ECB\n\n\n\n</query:AgencyID>\n" +
                        "        </query:CategorySchemeWhere>\n" +
                        "    </Query>\n\n\n\n\n" +
                        "</QueryMessage>";

        System.out.println(new XmlFormatter().format(unformattedXml));
    }

}

Просто обратите внимание, что этот ответ требует использования Xerces. Если вы не хотите добавлять эту зависимость, вы можете просто использовать стандартные библиотеки jdk и javax.xml.transform.Transformer (см. Мой ответ ниже)

khylo 17.12.2010 19:28

В 2008 году это был хороший ответ, но теперь все это можно сделать с помощью стандартных классов JDK, а не классов Apache. См. xerces.apache.org/xerces2-j/faq-general.html#faq-6. Да, это FAQ Xerces, но ответ касается стандартных классов JDK. В первоначальной реализации этих классов в версии 1.5 было много проблем, но начиная с версии 1.6 все работает нормально. Скопируйте пример LSSerializer в FAQ, отрежьте бит «...» и добавьте writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); после строки LSSerializer writer = ....

George Hawkins 04.05.2011 12:43

Я создал небольшой класс, используя пример Apache, на который @GeorgeHawkins дал ссылку. Не хватало способа инициализации переменной document, поэтому я подумал, что могу добавить замедление и сделать из этого быстрый пример. Дай мне знать, если я должен что-то изменить, pastebin.com/XL7932aC

samwell 16.07.2012 20:52

неверно, что вы можете сделать это только с помощью jdk. по крайней мере, не надежно. это зависит от некоторой внутренней реализации реестра, которая по умолчанию не активна с моим jdk7u72. так что вам все равно лучше использовать материал apache напрямую.

user1050755 17.11.2014 20:16

Вот решение без каких-либо зависимостей: stackoverflow.com/a/33541820/363573.

Stephan 05.11.2015 13:21

На самом деле я поддерживаю проект 2008 года LOL, спасибо!

MewX 05.10.2018 08:07

проблемы с utf-8

Alberto Acuña 27.06.2019 10:05

Чтобы избежать проверки xml и связанных накладных расходов, используйте builder.setEntityResolver((publicId, systemId) -> {return new InputSource(new StringReader(""));});

Olivier Faucheux 01.12.2020 23:15

Существует очень хорошая утилита командной строки XML под названием xmlstarlet (http://xmlstar.sourceforge.net/), которая может делать множество вещей, которыми пользуются многие люди.

Вы можете выполнить эту программу программно, используя Runtime.exec, а затем прочитать отформатированный выходной файл. У него больше возможностей и лучшая система отчетов об ошибках, чем может дать несколько строк кода Java.

скачать xmlstarlet: http://sourceforge.net/project/showfiles.php?group_id=66612&package_id=64589

Раньше я довольно много печатал, используя метод org.dom4j.io.OutputFormat.createPrettyPrint ()

public String prettyPrint(final String xml){  

    if (StringUtils.isBlank(xml)) {
        throw new RuntimeException("xml was null or blank in prettyPrint()");
    }

    final StringWriter sw;

    try {
        final OutputFormat format = OutputFormat.createPrettyPrint();
        final org.dom4j.Document document = DocumentHelper.parseText(xml);
        sw = new StringWriter();
        final XMLWriter writer = new XMLWriter(sw, format);
        writer.write(document);
    }
    catch (Exception e) {
        throw new RuntimeException("Error pretty printing xml:\n" + xml, e);
    }
    return sw.toString();
}

В моем случае принятое решение не имеет правильного отступа для вложенных тегов, а в этом случае.

Chase Seibert 06.11.2008 20:37

Я использовал это вместе с удалением всех конечных пробелов в конце строк: prettyPrintedString.replaceAll("\s+\n", "\n")

jediz 05.09.2017 14:32

Относительно комментария о том, что «сначала вы должны построить дерево DOM»: Нет, вам не нужно и не следует этого делать.

Вместо этого создайте StreamSource (new StreamSource (new StringReader (str)) и передайте его упомянутому преобразователю идентичности. Он будет использовать синтаксический анализатор SAX, и результат будет намного быстрее. В этом случае построение промежуточного дерева - это чистые накладные расходы. В противном случае лучший ответ - это хорошо.

Я полностью согласен: построение промежуточного дерева DOM - пустая трата памяти. Спасибо за ответ.

Florian F 26.10.2017 13:18

Если использование сторонней библиотеки XML в порядке, вы можете обойтись чем-то значительно более простым, чем то, что предлагает текущий получивший наибольшее количество голосовответы.

Было заявлено, что и ввод, и вывод должны быть строками, поэтому вот служебный метод, который делает именно это, реализованный с помощью библиотеки XOM:

import nu.xom.*;
import java.io.*;

[...]

public static String format(String xml) throws ParsingException, IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    Serializer serializer = new Serializer(out);
    serializer.setIndent(4);  // or whatever you like
    serializer.write(new Builder().build(xml, ""));
    return out.toString("UTF-8");
}

Я проверил, что он работает, и результаты не зависят от вашей версии JRE или чего-то подобного. Чтобы узнать, как настроить выходной формат по своему вкусу, взгляните на Serializer API.

На самом деле это получилось дольше, чем я думал - потребовалось несколько дополнительных строк, потому что Serializer хочет, чтобы OutputStream мог писать. Но обратите внимание, что здесь очень мало кода для реального тидлинга XML.

(Этот ответ является частью моей оценки XOM, которая была предложенный как один из вариантов в моем вопрос о лучшей библиотеке Java XML для замены dom4j. Для записи, с dom4j вы могли бы добиться этого с аналогичной легкостью, используя XMLWriter и OutputFormat. Редактировать: ... как показано на Ответ mlo55.)

Спасибо, это то, что я искал. Если у вас есть XML, уже проанализированный с помощью XOM в объекте «Документ», вы можете передать его напрямую в serializer.write (document);

Thibault D. 13.08.2013 10:07

более простое решение, основанное на этом ответе:

public static String prettyFormat(String input, int indent) {
    try {
        Source xmlInput = new StreamSource(new StringReader(input));
        StringWriter stringWriter = new StringWriter();
        StreamResult xmlOutput = new StreamResult(stringWriter);
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        transformerFactory.setAttribute("indent-number", indent);
        Transformer transformer = transformerFactory.newTransformer(); 
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.transform(xmlInput, xmlOutput);
        return xmlOutput.getWriter().toString();
    } catch (Exception e) {
        throw new RuntimeException(e); // simple exception handling, please review it
    }
}

public static String prettyFormat(String input) {
    return prettyFormat(input, 2);
}

прецедент:

prettyFormat("<root><child>aaa</child><child/></root>");

возвращает:

<?xml version = "1.0" encoding = "UTF-8"?>
<root>
  <child>aaa</child>
  <child/>
</root>

Это код, который я всегда использовал, но в этой компании он не работал, я предполагаю, что они используют другую библиотеку преобразования XML. Я создал фабрику отдельной линией, потом сделал factory.setAttribute("indent-number", 4);, и теперь он работает.

Adrian Smith 21.10.2010 17:25

Как сделать так, чтобы на выходе не было <?xml version = "1.0" encoding = "UTF-8"?>?

Thang Pham 19.07.2011 23:13

@ Гарри: transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATIO‌​N, "yes");

jjmontes 07.10.2011 13:06

Привет, я использую этот точный код и правильно форматирую, за исключением первого элемента. Итак, это: <?xml version = "1.0" encoding = "UTF-8"?><root> находится в одной строке. Есть идеи, почему?

CodyK 10.03.2015 21:28

@dfa: мне понравился комментарий // simple exception handling, please review it. Можете ли вы указать на некоторые ресурсы, которые рекомендуют этот тип обработки исключений? Спасибо.

John 23.09.2015 20:34

@Codemiester: Кажется, это ошибка (см. stackoverflow.com/a/18251901/3375325). Добавление transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes"); сработало для меня.

jansohn 10.08.2016 19:53

В Java 1.8 он ничего не делает (лишние пробелы не удаляются, отступы / новые строки не добавляются

azis.mrazish 17.01.2020 12:10

Этот фрагмент кода уязвим для внедрения XML-внешних сущностей (XXE). См .: cheatsheetseries.owasp.org/cheatsheets/…

fanbondi 09.02.2021 17:54

Хммм ... столкнулся с чем-то вроде этого и это известный баг ... просто добавьте это OutputProperty ..

transformer.setOutputProperty(OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "8");

Надеюсь это поможет ...

Откуда этот OutputPropertiesFactory?

helenov 20.10.2016 16:19

import com.sun.org.apache.xml.internal.serializer. *;

Gaurav 07.02.2020 12:46

Вот способ сделать это с помощью dom4j:

Импорт:

import org.dom4j.Document;  
import org.dom4j.DocumentHelper;  
import org.dom4j.io.OutputFormat;  
import org.dom4j.io.XMLWriter;

Код:

String xml = "<your xml='here'/>";  
Document doc = DocumentHelper.parseText(xml);  
StringWriter sw = new StringWriter();  
OutputFormat format = OutputFormat.createPrettyPrint();  
XMLWriter xw = new XMLWriter(sw, format);  
xw.write(doc);  
String result = sw.toString();

У меня это не сработало. У него просто что-то вроде: <?xml version... на одной строке, а все остальное на другой строке.

sixtyfootersdude 04.02.2012 00:39

Кевин Хакансон сказал: "Однако, если вы знаете, что ваша XML-строка действительна, и вы не хотите нести накладные расходы на память, связанные с синтаксическим анализом строки в DOM, а затем запускаете преобразование в DOM для возврата строки - вы можете просто символьный синтаксический анализ. Вставляйте новую строку и пробелы после каждого символа, сохраняйте счетчик отступов (для определения количества пробелов), который вы увеличиваете для каждого <...> и уменьшаете для каждого видимого вами. "

Согласовано. Такой подход намного быстрее и имеет гораздо меньше зависимостей.

Пример решения:

/**
 * XML utils, including formatting.
 */
public class XmlUtils
{
  private static XmlFormatter formatter = new XmlFormatter(2, 80);

  public static String formatXml(String s)
  {
    return formatter.format(s, 0);
  }

  public static String formatXml(String s, int initialIndent)
  {
    return formatter.format(s, initialIndent);
  }

  private static class XmlFormatter
  {
    private int indentNumChars;
    private int lineLength;
    private boolean singleLine;

    public XmlFormatter(int indentNumChars, int lineLength)
    {
      this.indentNumChars = indentNumChars;
      this.lineLength = lineLength;
    }

    public synchronized String format(String s, int initialIndent)
    {
      int indent = initialIndent;
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < s.length(); i++)
      {
        char currentChar = s.charAt(i);
        if (currentChar == '<')
        {
          char nextChar = s.charAt(i + 1);
          if (nextChar == '/')
            indent -= indentNumChars;
          if (!singleLine)   // Don't indent before closing element if we're creating opening and closing elements on a single line.
            sb.append(buildWhitespace(indent));
          if (nextChar != '?' && nextChar != '!' && nextChar != '/')
            indent += indentNumChars;
          singleLine = false;  // Reset flag.
        }
        sb.append(currentChar);
        if (currentChar == '>')
        {
          if (s.charAt(i - 1) == '/')
          {
            indent -= indentNumChars;
            sb.append("\n");
          }
          else
          {
            int nextStartElementPos = s.indexOf('<', i);
            if (nextStartElementPos > i + 1)
            {
              String textBetweenElements = s.substring(i + 1, nextStartElementPos);

              // If the space between elements is solely newlines, let them through to preserve additional newlines in source document.
              if (textBetweenElements.replaceAll("\n", "").length() == 0)
              {
                sb.append(textBetweenElements + "\n");
              }
              // Put tags and text on a single line if the text is short.
              else if (textBetweenElements.length() <= lineLength * 0.5)
              {
                sb.append(textBetweenElements);
                singleLine = true;
              }
              // For larger amounts of text, wrap lines to a maximum line length.
              else
              {
                sb.append("\n" + lineWrap(textBetweenElements, lineLength, indent, null) + "\n");
              }
              i = nextStartElementPos - 1;
            }
            else
            {
              sb.append("\n");
            }
          }
        }
      }
      return sb.toString();
    }
  }

  private static String buildWhitespace(int numChars)
  {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < numChars; i++)
      sb.append(" ");
    return sb.toString();
  }

  /**
   * Wraps the supplied text to the specified line length.
   * @lineLength the maximum length of each line in the returned string (not including indent if specified).
   * @indent optional number of whitespace characters to prepend to each line before the text.
   * @linePrefix optional string to append to the indent (before the text).
   * @returns the supplied text wrapped so that no line exceeds the specified line length + indent, optionally with
   * indent and prefix applied to each line.
   */
  private static String lineWrap(String s, int lineLength, Integer indent, String linePrefix)
  {
    if (s == null)
      return null;

    StringBuilder sb = new StringBuilder();
    int lineStartPos = 0;
    int lineEndPos;
    boolean firstLine = true;
    while(lineStartPos < s.length())
    {
      if (!firstLine)
        sb.append("\n");
      else
        firstLine = false;

      if (lineStartPos + lineLength > s.length())
        lineEndPos = s.length() - 1;
      else
      {
        lineEndPos = lineStartPos + lineLength - 1;
        while (lineEndPos > lineStartPos && (s.charAt(lineEndPos) != ' ' && s.charAt(lineEndPos) != '\t'))
          lineEndPos--;
      }
      sb.append(buildWhitespace(indent));
      if (linePrefix != null)
        sb.append(linePrefix);

      sb.append(s.substring(lineStartPos, lineEndPos + 1));
      lineStartPos = lineEndPos + 1;
    }
    return sb.toString();
  }

  // other utils removed for brevity
}

Так и должно быть. Форматирование на лету на строковом уровне. Это единственное решение, которое отформатирует недопустимый или неполный XML.

Florian F 26.10.2017 13:20

У меня была такая же проблема, и у меня большой успех с JTidy (http://jtidy.sourceforge.net/index.html)

Пример:

Tidy t = new Tidy();
t.setIndentContent(true);
Document d = t.parseDOM(
    new ByteArrayInputStream("HTML goes here", null);

OutputStream out = new ByteArrayOutputStream();
t.pprint(d, out);
String html = out.toString();

Просто обратите внимание, что ответ с самым высоким рейтингом требует использования xerces.

Если вы не хотите добавлять эту внешнюю зависимость, вы можете просто использовать стандартные библиотеки jdk (которые на самом деле построены с использованием xerces внутри).

N.B. Была ошибка с jdk версии 1.5 см. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446, но теперь она решена.,

(Обратите внимание, что при возникновении ошибки будет возвращен исходный текст)

package com.test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.stream.StreamResult;

import org.xml.sax.InputSource;

public class XmlTest {
    public static void main(String[] args) {
        XmlTest t = new XmlTest();
        System.out.println(t.formatXml("<a><b><c/><d>text D</d><e value='0'/></b></a>"));
    }

    public String formatXml(String xml){
        try{
            Transformer serializer= SAXTransformerFactory.newInstance().newTransformer();
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            //serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
            //serializer.setOutputProperty("{http://xml.customer.org/xslt}indent-amount", "2");
            Source xmlSource=new SAXSource(new InputSource(new ByteArrayInputStream(xml.getBytes())));
            StreamResult res =  new StreamResult(new ByteArrayOutputStream());            
            serializer.transform(xmlSource, res);
            return new String(((ByteArrayOutputStream)res.getOutputStream()).toByteArray());
        }catch(Exception e){
            //TODO log error
            return xml;
        }
    }

}

В этом случае левая вкладка не используется. Все теги начинаются с первого символа строки, как и обычный текст.

Ruslan 23.12.2010 12:57

разве вам не нужно указывать кодировку при преобразовании назад и вперед между байтами и строкой?

Will Glass 02.12.2011 05:18

Не должно быть необходимости конвертировать из и в байтовые массивы / String. По крайней мере, при этом вам нужно будет указать кодировку. Лучшим вариантом было бы использовать классы StringReader и StringWriter, заключенные в InputSource и StreamResult.

maximdim 21.12.2012 20:00

не работает. вам нужно возиться с некоторой внутренней реализацией реестра.

user1050755 17.11.2014 20:14

Вот более простой вариант этого решения: stackoverflow.com/a/33541820/363573

Stephan 05.11.2015 13:37

Используя scala:

import xml._
val xml = XML.loadString("<tag><nested>hello</nested></tag>")
val formatted = new PrettyPrinter(150, 2).format(xml)
println(formatted)

Вы можете сделать это и на Java, если вы полагаетесь на scala-library.jar. Выглядит это так:

import scala.xml.*;

public class FormatXML {
    public static void main(String[] args) {
        String unformattedXml = "<tag><nested>hello</nested></tag>";
        PrettyPrinter pp = new PrettyPrinter(150, 3);
        String formatted = pp.format(XML.loadString(unformattedXml), TopScope$.MODULE$);
        System.out.println(formatted);
    }
}

Объект PrettyPrinter состоит из двух целых чисел, первое из которых - максимальная длина строки, а второе - шаг отступа.

Просто для справки в будущем вот решение, которое сработало для меня (благодаря комментарию, который @George Hawkins опубликовал в одном из ответов):

DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
LSSerializer writer = impl.createLSSerializer();
writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);
LSOutput output = impl.createLSOutput();
ByteArrayOutputStream out = new ByteArrayOutputStream();
output.setByteStream(out);
writer.write(document, output);
String xmlStr = new String(out.toByteArray());

Я обнаружил, что в Java 1.6.0_32 обычный метод красивой печати XML нить (с использованием преобразователя с нулевым значением или идентификатором xslt) ведет себя не так, как хотелось бы, если теги просто разделяются пробелами, а не без разделения текст. Я безуспешно пытался использовать <xsl:strip-space elements = "*"/> в своем шаблоне. Самым простым решением, которое я нашел, было разделить пространство так, как я хотел, с помощью SAXSource и XML-фильтра. Поскольку мое решение было для ведения журнала, я также расширил его для работы с неполными фрагментами XML. Обратите внимание, что обычный метод работает нормально, если вы используете DOMSource, но я не хотел использовать его из-за неполноты и накладных расходов на память.

public static class WhitespaceIgnoreFilter extends XMLFilterImpl
{

    @Override
    public void ignorableWhitespace(char[] arg0,
                                    int arg1,
                                    int arg2) throws SAXException
    {
        //Ignore it then...
    }

    @Override
    public void characters( char[] ch,
                            int start,
                            int length) throws SAXException
    {
        if (!new String(ch, start, length).trim().equals("")) 
               super.characters(ch, start, length); 
    }
}

public static String prettyXML(String logMsg, boolean allowBadlyFormedFragments) throws SAXException, IOException, TransformerException
    {
        TransformerFactory transFactory = TransformerFactory.newInstance();
        transFactory.setAttribute("indent-number", new Integer(2));
        Transformer transformer = transFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
        StringWriter out = new StringWriter();
        XMLReader masterParser = SAXHelper.getSAXParser(true);
        XMLFilter parser = new WhitespaceIgnoreFilter();
        parser.setParent(masterParser);

        if (allowBadlyFormedFragments)
        {
            transformer.setErrorListener(new ErrorListener()
            {
                @Override
                public void warning(TransformerException exception) throws TransformerException
                {
                }

                @Override
                public void fatalError(TransformerException exception) throws TransformerException
                {
                }

                @Override
                public void error(TransformerException exception) throws TransformerException
                {
                }
            });
        }

        try
        {
            transformer.transform(new SAXSource(parser, new InputSource(new StringReader(logMsg))), new StreamResult(out));
        }
        catch (TransformerException e)
        {
            if (e.getCause() != null && e.getCause() instanceof SAXParseException)
            {
                if (!allowBadlyFormedFragments || !"XML document structures must start and end within the same entity.".equals(e.getCause().getMessage()))
                {
                    throw e;
                }
            }
            else
            {
                throw e;
            }
        }
        out.flush();
        return out.toString();
    }
Ответ принят как подходящий

Сейчас 2012 год, и Java может делать больше, чем раньше, с XML, я хотел бы добавить альтернативу моему принятому ответу. Это не имеет зависимостей вне Java 6.

import org.w3c.dom.Node;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;

/**
 * Pretty-prints xml, supplied as a string.
 * <p/>
 * eg.
 * <code>
 * String formattedXml = new XmlFormatter().format("<tag><nested>hello</nested></tag>");
 * </code>
 */
public class XmlFormatter {

    public String format(String xml) {

        try {
            final InputSource src = new InputSource(new StringReader(xml));
            final Node document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src).getDocumentElement();
            final Boolean keepDeclaration = Boolean.valueOf(xml.startsWith("<?xml"));

        //May need this: System.setProperty(DOMImplementationRegistry.PROPERTY,"com.sun.org.apache.xerces.internal.dom.DOMImplementationSourceImpl");


            final DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
            final DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
            final LSSerializer writer = impl.createLSSerializer();

            writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); // Set this to true if the output needs to be beautified.
            writer.getDomConfig().setParameter("xml-declaration", keepDeclaration); // Set this to true if the declaration is needed to be outputted.

            return writer.writeToString(document);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        String unformattedXml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?><QueryMessage\n" +
                        "        xmlns=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message\"\n" +
                        "        xmlns:query=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/query\">\n" +
                        "    <Query>\n" +
                        "        <query:CategorySchemeWhere>\n" +
                        "   \t\t\t\t\t         <query:AgencyID>ECB\n\n\n\n</query:AgencyID>\n" +
                        "        </query:CategorySchemeWhere>\n" +
                        "    </Query>\n\n\n\n\n" +
                        "</QueryMessage>";

        System.out.println(new XmlFormatter().format(unformattedXml));
    }
}

Без отступов, но он работает со следующим: System.setProperty (DOMImplementation Registry.PROPERTY, «com.s‌ un.org.apache.xerces‌ .internal.dom.DOMImp‌ lementationSourceImp‌ l»);

ggb667 20.03.2013 16:21

Я попробовал это решение и обнаружил, что возвращаемая строка XML всегда имеет UTF-16. Когда документ сериализуется обратно в строку, появляется следующая строка кода: ser._format.setEncoding("UTF-16"); Сейчас это может быть стандартом, но для системы, с которой я работаю, используется UTF-8. Кто-нибудь знает, как сохранить кодировку из исходной строки XML?

Dan Temple 25.02.2014 13:21

@DanTemple Похоже, вам нужно использовать LSOutput для управления кодировкой. См. chipkillmar.net/2009/03/25/pretty-print-xml-from-a-dom

Joshua Davis 12.03.2014 23:50

@JoshuaDavis Спасибо, это работает для установки кодировки ответа. Мне нужно добавить что-то, что передает исходную кодировку красивому принтеру, если я хочу сохранить кодировку исходной строки XML.

Dan Temple 13.03.2014 13:35

@JoshA. Есть однострочный код с использованием XMLBeam: System.out.println(new XBProjector().projectXMLString("<xml><foo><bar/></foo></xml>‌​", DOMAccess.class).asString());

Cfx 27.10.2014 15:04

Я обнаружил, что это переносит строки на определенную длину - есть ли способ предотвратить перенос?

eeijlar 05.02.2015 14:49

это добавляет спецификацию FEFF в начало возвращаемой строки.

Marcus Junius Brutus 08.03.2015 00:55

Я пытался использовать это в Android, но не могу найти пакет `DOMImplementation Registry. Я использую java 8.

Chintan Soni 10.05.2015 10:53

У меня возникли проблемы с этим фрагментом, связанные с экранированными значениями атрибутов. Меньшая escape-последовательность &lt; была правильно сохранена, в то время как последовательность больше чем &gt; была преобразована в фактический знак >.

Julian Sievers 20.01.2016 17:25

да, эта работа на jdk8 без исключения вместо следующего рейтингового ответа.

Yura 18.05.2016 15:48

Чтобы установить кодировку: LSOutput output = impl.createLSOutput (); output.setEncoding ("UTF-8"); output.setByteStream (новый ByteArrayOutputStream ()); Writer.write (документ, вывод); вернуть output.getByteStream (). toString ();

Ali Cheaito 01.09.2017 22:56

Не работал над JDK 8, развернутым в JBoss EAP 7 / Wildfly 10, получить класс не найден исключения com.sun.org.apache.xerces.internal.dom.DOMXSImplementationSo‌​urceImpl - похоже, связано с этим issues.jboss.org/browse/WFLY-4416 - но я не собираюсь иметь дело с добавлением дополнительной библиотеки или чего-то еще - мне не нужно, чтобы это было так плохо напечатано .

JGlass 12.07.2018 21:20

спасибо, что вы также включили список импорта, так много конфликтующих пакетов доступно, чтобы понять комбинацию, необходимую в противном случае

Leon 28.03.2019 17:39

Для тех, кто ищет быстрое и грязное решение, для которого не требуется, чтобы XML был на 100% достоверным. например в случае ведения журнала REST / SOAP (никогда не знаешь, что отправляют другие ;-))

Я нашел и продвинул фрагмент кода, который я нашел в Интернете, который, как мне кажется, все еще отсутствует здесь как допустимый подход:

public static String prettyPrintXMLAsString(String xmlString) {
    /* Remove new lines */
    final String LINE_BREAK = "\n";
    xmlString = xmlString.replaceAll(LINE_BREAK, "");
    StringBuffer prettyPrintXml = new StringBuffer();
    /* Group the xml tags */
    Pattern pattern = Pattern.compile("(<[^/][^>]+>)?([^<]*)(</[^>]+>)?(<[^/][^>]+/>)?");
    Matcher matcher = pattern.matcher(xmlString);
    int tabCount = 0;
    while (matcher.find()) {
        String str1 = (null == matcher.group(1) || "null".equals(matcher.group())) ? "" : matcher.group(1);
        String str2 = (null == matcher.group(2) || "null".equals(matcher.group())) ? "" : matcher.group(2);
        String str3 = (null == matcher.group(3) || "null".equals(matcher.group())) ? "" : matcher.group(3);
        String str4 = (null == matcher.group(4) || "null".equals(matcher.group())) ? "" : matcher.group(4);

        if (matcher.group() != null && !matcher.group().trim().equals("")) {
            printTabs(tabCount, prettyPrintXml);
            if (!str1.equals("") && str3.equals("")) {
                ++tabCount;
            }
            if (str1.equals("") && !str3.equals("")) {
                --tabCount;
                prettyPrintXml.deleteCharAt(prettyPrintXml.length() - 1);
            }

            prettyPrintXml.append(str1);
            prettyPrintXml.append(str2);
            prettyPrintXml.append(str3);
            if (!str4.equals("")) {
                prettyPrintXml.append(LINE_BREAK);
                printTabs(tabCount, prettyPrintXml);
                prettyPrintXml.append(str4);
            }
            prettyPrintXml.append(LINE_BREAK);
        }
    }
    return prettyPrintXml.toString();
}

private static void printTabs(int count, StringBuffer stringBuffer) {
    for (int i = 0; i < count; i++) {
        stringBuffer.append("\t");
    }
}

public static void main(String[] args) {
    String x = new String(
            "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><soap:Fault><faultcode>soap:Client</faultcode><faultstring>INVALID_MESSAGE</faultstring><detail><ns3:XcbSoapFault xmlns=\"\" xmlns:ns3=\"http://www.someapp.eu/xcb/types/xcb/v1\"><CauseCode>20007</CauseCode><CauseText>INVALID_MESSAGE</CauseText><DebugInfo>Problems creating SAAJ object model</DebugInfo></ns3:XcbSoapFault></detail></soap:Fault></soap:Body></soap:Envelope>");
    System.out.println(prettyPrintXMLAsString(x));
}

вот результат:

<soap:Envelope xmlns:soap = "http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <soap:Fault>
        <faultcode>soap:Client</faultcode>
        <faultstring>INVALID_MESSAGE</faultstring>
        <detail>
            <ns3:XcbSoapFault xmlns = "" xmlns:ns3 = "http://www.someapp.eu/xcb/types/xcb/v1">
                <CauseCode>20007</CauseCode>
                <CauseText>INVALID_MESSAGE</CauseText>
                <DebugInfo>Problems creating SAAJ object model</DebugInfo>
            </ns3:XcbSoapFault>
        </detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

Если вы уверены, что у вас есть допустимый XML, этот простой и позволяет избежать деревьев XML DOM. Возможно, есть какие-то ошибки, комментируйте, если что-то видите

public String prettyPrint(String xml) {
            if (xml == null || xml.trim().length() == 0) return "";

            int stack = 0;
            StringBuilder pretty = new StringBuilder();
            String[] rows = xml.trim().replaceAll(">", ">\n").replaceAll("<", "\n<").split("\n");

            for (int i = 0; i < rows.length; i++) {
                    if (rows[i] == null || rows[i].trim().length() == 0) continue;

                    String row = rows[i].trim();
                    if (row.startsWith("<?")) {
                            // xml version tag
                            pretty.append(row + "\n");
                    } else if (row.startsWith("</")) {
                            // closing tag
                            String indent = repeatString("    ", --stack);
                            pretty.append(indent + row + "\n");
                    } else if (row.startsWith("<")) {
                            // starting tag
                            String indent = repeatString("    ", stack++);
                            pretty.append(indent + row + "\n");
                    } else {
                            // tag data
                            String indent = repeatString("    ", stack);
                            pretty.append(indent + row + "\n");
                    }
            }

            return pretty.toString().trim();
    }

где метод repeatString ..?

user1912935 25.06.2015 14:07

частный статический String repeatString (int stack) {StringBuilder indent = new StringBuilder (); для (int я = 0; я <стек; я ++) {indent.append (""); } return indent.toString (); }

codeskraps 25.06.2015 17:17

Да [user1912935], то, что написал @codeskraps, должно быть достаточно простым :)

milosmns 25.06.2015 19:36

Конкатенация со StringBuilder внутри цикла: плохая практика.

james.garriss 22.12.2015 17:00

@ james.garriss Но очень легко разделить на новые строки, это просто иллюстрирует простой подход без каких-либо деревьев DOM.

milosmns 30.11.2018 14:02

немного улучшенная версия от Милосмнс ...

public static String getPrettyXml(String xml) {
    if (xml == null || xml.trim().length() == 0) return "";

    int stack = 0;
    StringBuilder pretty = new StringBuilder();
    String[] rows = xml.trim().replaceAll(">", ">\n").replaceAll("<", "\n<").split("\n");

    for (int i = 0; i < rows.length; i++) {
        if (rows[i] == null || rows[i].trim().length() == 0) continue;

        String row = rows[i].trim();
        if (row.startsWith("<?")) {
            pretty.append(row + "\n");
        } else if (row.startsWith("</")) {
            String indent = repeatString(--stack);
            pretty.append(indent + row + "\n");
        } else if (row.startsWith("<") && row.endsWith("/>") == false) {
            String indent = repeatString(stack++);
            pretty.append(indent + row + "\n");
            if (row.endsWith("]]>")) stack--;
        } else {
            String indent = repeatString(stack);
            pretty.append(indent + row + "\n");
        }
    }

    return pretty.toString().trim();
}

private static String repeatString(int stack) {
     StringBuilder indent = new StringBuilder();
     for (int i = 0; i < stack; i++) {
        indent.append(" ");
     }
     return indent.toString();
} 

где repeatString (stack ++); метод ..?

user1912935 25.06.2015 12:59

частный статический String repeatString (int stack) {StringBuilder indent = new StringBuilder (); для (int я = 0; я <стек; я ++) {indent.append (""); } return indent.toString (); }

codeskraps 25.06.2015 17:16

Отступы в конечных тегах работают некорректно. Вам необходимо заменить часть } else if (row.startsWith("</")) { на эту: else if (row.startsWith("</")) { String indent = repeatIdent(--stack); if (pretty.charAt(pretty.length() - 1) == '\n') { pretty.append(indent + row + "\n"); } else { pretty.append(row + "\n"); } }

Csaba Tenkes 28.12.2018 13:17

Решения, которые я нашел здесь для Java 1.6+, не переформатируют код, если он уже отформатирован. Тот, который сработал для меня (и переформатировал уже отформатированный код), был следующим.

import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.w3c.dom.Element;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.IOException;
import java.io.StringReader;

public class XmlUtils {
    public static String toCanonicalXml(String xml) throws InvalidCanonicalizerException, ParserConfigurationException, SAXException, CanonicalizationException, IOException {
        Canonicalizer canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
        byte canonXmlBytes[] = canon.canonicalize(xml.getBytes());
        return new String(canonXmlBytes);
    }

    public static String prettyFormat(String input) throws TransformerException, ParserConfigurationException, IOException, SAXException, InstantiationException, IllegalAccessException, ClassNotFoundException {
        InputSource src = new InputSource(new StringReader(input));
        Element document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src).getDocumentElement();
        Boolean keepDeclaration = input.startsWith("<?xml");
        DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
        DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
        LSSerializer writer = impl.createLSSerializer();
        writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);
        writer.getDomConfig().setParameter("xml-declaration", keepDeclaration);
        return writer.writeToString(document);
    }
}

Это хороший инструмент для использования в ваших модульных тестах для полнострочного сравнения xml.

private void assertXMLEqual(String expected, String actual) throws ParserConfigurationException, IOException, SAXException, CanonicalizationException, InvalidCanonicalizerException, TransformerException, IllegalAccessException, ClassNotFoundException, InstantiationException {
    String canonicalExpected = prettyFormat(toCanonicalXml(expected));
    String canonicalActual = prettyFormat(toCanonicalXml(actual));
    assertEquals(canonicalExpected, canonicalActual);
}

В качестве альтернативы ответам Максимум, коды, Дэвид Исли и Милосмнс взгляните на мою легкую, высокопроизводительную библиотеку симпатичных принтеров: xml-форматировщик

// construct lightweight, threadsafe, instance
PrettyPrinter prettyPrinter = PrettyPrinterBuilder.newPrettyPrinter().build();

StringBuilder buffer = new StringBuilder();
String xml = ..; // also works with char[] or Reader

if (prettyPrinter.process(xml, buffer)) {
     // valid XML, print buffer
} else {
     // invalid XML, print xml
}

Иногда, например, при запуске смоделированных служб SOAP непосредственно из файла, хорошо иметь симпатичный принтер, который также обрабатывает уже хорошо напечатанный XML:

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

Как отмечали некоторые, красивая печать - это просто способ представления XML в более удобочитаемой форме - пробелы строго не принадлежат вашим данным XML.

Библиотека предназначена для красивой печати для целей ведения журнала, а также включает функции для фильтрации (удаление поддерева / анонимизация) и красивой печати XML в узлах CDATA и Text.

Я видел один ответ с использованием Scala, так что вот еще один в Groovy, на всякий случай, если кому-то это интересно. Отступ по умолчанию составляет 2 шага, конструктору XmlNodePrinter можно передать и другое значение.

def xml = "<tag><nested>hello</nested></tag>"
def stringWriter = new StringWriter()
def node = new XmlParser().parseText(xml);
new XmlNodePrinter(new PrintWriter(stringWriter)).print(node)
println stringWriter.toString()

Использование из Java, если groovy jar находится в пути к классам

  String xml = "<tag><nested>hello</nested></tag>";
  StringWriter stringWriter = new StringWriter();
  Node node = new XmlParser().parseText(xml);
  new XmlNodePrinter(new PrintWriter(stringWriter)).print(node);
  System.out.println(stringWriter.toString());

Использование jdom2: http://www.jdom.org/

import java.io.StringReader;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

String prettyXml = new XMLOutputter(Format.getPrettyFormat()).
                         outputString(new SAXBuilder().build(new StringReader(uglyXml)));

Все вышеперечисленные решения у меня не работали, потом я нашел этот http://myshittycode.com/2014/02/10/java-properly-indenting-xml-string/

Подсказка - удалить пробелы с помощью XPath

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

try {
    Document document = DocumentBuilderFactory.newInstance()
            .newDocumentBuilder()
            .parse(new InputSource(new ByteArrayInputStream(xml.getBytes("utf-8"))));

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

    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

    StringWriter stringWriter = new StringWriter();
    StreamResult streamResult = new StreamResult(stringWriter);

    transformer.transform(new DOMSource(document), streamResult);

    System.out.println(stringWriter.toString());
}
catch (Exception e) {
    e.printStackTrace();
}

Обратите внимание, что использование свойства '{xml.apache.org/xslt} indent-amount' привяжет вас к конкретной реализации преобразователя.

vallismortis 17.06.2015 17:28

Из всех решений это сработало лучше всего. В моем XML уже были пробелы и новые строки, к тому же я не хотел добавлять больше зависимостей в свой проект. Мне жаль, что мне не пришлось разбирать XML, ну да ладно.

Fabio 10.03.2016 22:25

Еще одно решение, которое работает для нас

import java.io.StringWriter;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

**
 * Pretty Print XML String
 * 
 * @param inputXmlString
 * @return
 */
public static String prettyPrintXml(String xml) {

    final StringWriter sw;

    try {
        final OutputFormat format = OutputFormat.createPrettyPrint();
        final org.dom4j.Document document = DocumentHelper.parseText(xml);
        sw = new StringWriter();
        final XMLWriter writer = new XMLWriter(sw, format);
        writer.write(document);
    }
    catch (Exception e) {
        throw new RuntimeException("Error pretty printing xml:\n" + xml, e);
    }
    return sw.toString();
}

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

String leastPrettifiedXml = uglyXml.replaceAll("><", ">\n<");

Код хорош, а не результат из-за отсутствия отступов.


(Для решений с отступом см. Другие ответы.)

Хммм ... Просто подумай, кому может понадобиться такое решение? Единственная область, которую я вижу, - это данные, которые мы получаем от некоторых веб-сервисов, и просто для проверки этих данных и их достоверности разработчику или тестировщику могут понадобиться такие простые. В противном случае не лучший вариант ....

Sudhakar Chavali 08.02.2018 19:22

@SudhakarChavali, я разработчик. мне это может понадобиться для грязных взломов println () и log.debug (); то есть иногда я могу использовать файлы журнала только из ограниченной серверной среды (с интерфейсом веб-администратора вместо доступа к оболочке) вместо разумной пошаговой отладки программы.

comonad 19.02.2018 20:23

Попробуй это:

 try
                    {
                        TransformerFactory transFactory = TransformerFactory.newInstance();
                        Transformer transformer = null;
                        transformer = transFactory.newTransformer();
                        StringWriter buffer = new StringWriter();
                        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
                        transformer.transform(new DOMSource(element),
                                  new StreamResult(buffer)); 
                        String str = buffer.toString();
                        System.out.println("XML INSIDE IS #########################################"+str);
                        return element;
                    }
                    catch (TransformerConfigurationException e)
                    {
                        e.printStackTrace();
                    }
                    catch (TransformerException e)
                    {
                        e.printStackTrace();
                    }

Не вижу разницы с некоторыми из уже опубликованных ответов.

Olivier Cailloux 14.07.2020 19:35

Этот код ниже работает отлично

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

String formattedXml1 = prettyFormat("<root><child>aaa</child><child/></root>");

public static String prettyFormat(String input) {
    return prettyFormat(input, "2");
}

public static String prettyFormat(String input, String indent) {
    Source xmlInput = new StreamSource(new StringReader(input));
    StringWriter stringWriter = new StringWriter();
    try {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", indent);
        transformer.transform(xmlInput, new StreamResult(stringWriter));

        String pretty = stringWriter.toString();
        pretty = pretty.replace("\r\n", "\n");
        return pretty;              
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Я должен был сначала поискать эту страницу, прежде чем придумывать собственное решение! Во всяком случае, мой использует рекурсию Java для анализа xml-страницы. Этот код полностью самодостаточен и не полагается на сторонние библиотеки. Также .. он использует рекурсию!

// you call this method passing in the xml text
public static void prettyPrint(String text){
    prettyPrint(text, 0);
}

// "index" corresponds to the number of levels of nesting and/or the number of tabs to print before printing the tag
public static void prettyPrint(String xmlText, int index){
    boolean foundTagStart = false;
    StringBuilder tagChars = new StringBuilder();
    String startTag = "";
    String endTag = "";
    String[] chars = xmlText.split("");
    // find the next start tag
    for(String ch : chars){
        if (ch.equalsIgnoreCase("<")){
            tagChars.append(ch);
            foundTagStart = true;
        } else if (ch.equalsIgnoreCase(">") && foundTagStart){
            startTag = tagChars.append(ch).toString();
            String tempTag = startTag;
            endTag = (tempTag.contains("\"") ? (tempTag.split(" ")[0] + ">") : tempTag).replace("<", "</"); // <startTag attr1=1 attr2=2> => </startTag>
            break;
        } else if (foundTagStart){
            tagChars.append(ch);
        }
    }
    // once start and end tag are calculated, print start tag, then content, then end tag
    if (foundTagStart){
        int startIndex = xmlText.indexOf(startTag);
        int endIndex = xmlText.indexOf(endTag);
        // handle if matching tags NOT found
        if ((startIndex < 0) || (endIndex < 0)){
            if (startIndex < 0) {
                // no start tag found
                return;
            } else {
                // start tag found, no end tag found (handles single tags aka "<mytag/>" or "<?xml ...>")
                printTabs(index);
                System.out.println(startTag);
                // move on to the next tag
                // NOTE: "index" (not index+1) because next tag is on same level as this one
                prettyPrint(xmlText.substring(startIndex+startTag.length(), xmlText.length()), index);
                return;
            }
        // handle when matching tags found
        } else {
            String content = xmlText.substring(startIndex+startTag.length(), endIndex);
            boolean isTagContainsTags = content.contains("<"); // content contains tags
            printTabs(index);
            if (isTagContainsTags){ // ie: <tag1><tag2>stuff</tag2></tag1>
                System.out.println(startTag);
                prettyPrint(content, index+1); // "index+1" because "content" is nested
                printTabs(index);
            } else {
                System.out.print(startTag); // ie: <tag1>stuff</tag1> or <tag1></tag1>
                System.out.print(content);
            }
            System.out.println(endTag);
            int nextIndex = endIndex + endTag.length();
            if (xmlText.length() > nextIndex){ // if there are more tags on this level, continue
                prettyPrint(xmlText.substring(nextIndex, xmlText.length()), index);
            }
        }
    } else {
        System.out.print(xmlText);
    }
}

private static void printTabs(int counter){
    while(counter-- > 0){ 
        System.out.print("\t");
    }
}

Underscore-java, U.formatXml (xml) также не полагается на сторонние библиотеки.

Valentyn Kolesnikov 29.06.2019 07:39

Я смешиваю их все и пишу одну небольшую программу. Он читает из XML-файла и распечатывает его. Просто вместо xzy укажите путь к файлу.

    public static void main(String[] args) throws Exception {
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setValidating(false);
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(new FileInputStream(new File("C:/Users/xyz.xml")));
    prettyPrint(doc);

}

private static String prettyPrint(Document document)
        throws TransformerException {
    TransformerFactory transformerFactory = TransformerFactory
            .newInstance();
    Transformer transformer = transformerFactory.newTransformer();
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
    DOMSource source = new DOMSource(document);
    StringWriter strWriter = new StringWriter();
    StreamResult result = new StreamResult(strWriter);transformer.transform(source, result);
    System.out.println(strWriter.getBuffer().toString());

    return strWriter.getBuffer().toString();

}

Подчеркивание-java имеет статический метод U.formatXml(string). Я сопровождаю проект. Живой пример

import com.github.underscore.lodash.U;

public class MyClass {
    public static void main(String args[]) {
        String xml = "<tag><nested>hello</nested></tag>";

        System.out.println(U.formatXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>" + xml + "</root>"));
    }
}

Выход:

<?xml version = "1.0" encoding = "UTF-8"?>
<root>
   <tag>
      <nested>hello</nested>
   </tag>
</root>

Это круто!

senyor 02.10.2019 20:58

Я всегда использую следующую функцию:

public static String prettyPrintXml(String xmlStringToBeFormatted) {
    String formattedXmlString = null;
    try {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setValidating(true);
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        InputSource inputSource = new InputSource(new StringReader(xmlStringToBeFormatted));
        Document document = documentBuilder.parse(inputSource);

        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        StreamResult streamResult = new StreamResult(new StringWriter());
        DOMSource dOMSource = new DOMSource(document);
        transformer.transform(dOMSource, streamResult);
        formattedXmlString = streamResult.getWriter().toString().trim();
    } catch (Exception ex) {
        StringWriter sw = new StringWriter();
        ex.printStackTrace(new PrintWriter(sw));
        System.err.println(sw.toString());
    }
    return formattedXmlString;
}

Это важно: transformer.setOutputProperty ("{xml.apache.org/xslt} indent-a‌ mount", "2"); Если его нет, все элементы выровнены по левому краю

Hank Lapidez 07.02.2021 15:44

@HankLapidez Это правда.

Ben 08.02.2021 12:11

Я пытался добиться чего-то похожего, но без какой-либо внешней зависимости. Приложение уже использовало DOM для форматирования только для записи XML!

Вот мой образец фрагмента

public void formatXML(final String unformattedXML) {
    final int length = unformattedXML.length();
    final int indentSpace = 3;
    final StringBuilder newString = new StringBuilder(length + length / 10);
    final char space = ' ';
    int i = 0;
    int indentCount = 0;
    char currentChar = unformattedXML.charAt(i++);
    char previousChar = currentChar;
    boolean nodeStarted = true;
    newString.append(currentChar);
    for (; i < length - 1;) {
        currentChar = unformattedXML.charAt(i++);
        if (((int) currentChar < 33) && !nodeStarted) {
            continue;
        }
        switch (currentChar) {
        case '<':
            if ('>' == previousChar && '/' != unformattedXML.charAt(i - 1) && '/' != unformattedXML.charAt(i) && '!' != unformattedXML.charAt(i)) {
                indentCount++;
            }
            newString.append(System.lineSeparator());
            for (int j = indentCount * indentSpace; j > 0; j--) {
                newString.append(space);
            }
            newString.append(currentChar);
            nodeStarted = true;
            break;
        case '>':
            newString.append(currentChar);
            nodeStarted = false;
            break;
        case '/':
            if ('<' == previousChar || '>' == unformattedXML.charAt(i)) {
                indentCount--;
            }
            newString.append(currentChar);
            break;
        default:
            newString.append(currentChar);
        }
        previousChar = currentChar;
    }
    newString.append(unformattedXML.charAt(length - 1));
    System.out.println(newString.toString());
}

Удаляет пробелы в тексте. Пример: <text> \ n некоторый пример lol \ n <text> после преобразования: <text> someexamplelol <test>

Maciej Pulikowski 12.01.2020 15:03

да, и у него есть другие дефекты, такие как обработка комментариев, DTD, если таковые имеются, и т. д. Однако, исправив это, я смог получить приемлемый (кроме сложных элементов, таких как <text>, снова некоторый сложный <b> текст < / b> а то ничего </text>) логика работает. У меня нет кода под рукой, найдется немного свободного времени, чтобы написать еще раз

Faisal K 13.01.2020 16:14

Пожалуйста, укажите, как это решение улучшает уже существующие ответы, в противном случае оно просто добавляет шума.

Olivier Cailloux 14.07.2020 19:36

Другие вопросы по теме