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

Если для вашего источника требуется только захват / изменение вызовов, самым простым решением является объединение файла заголовка (intercept.h) с помощью:

#ifdef INTERCEPT
    #define getObjectName(x) myGetObectName(x)
#endif

и реализации функции следующим образом (в intercept.c, который не включает intercept.h):

const char *myGetObjectName (object *anObject) {
    if (anObject == NULL)
        return "(null)";
    else
        return getObjectName(anObject);
}

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

#include "intercept.h"

вверху.

Затем, когда вы компилируете с помощью «-DINTERCEPT», все файлы будут вызывать вашу функцию, а не реальную, и ваша функция все равно может вызвать реальный.

Компиляция без «-DINTERCEPT» предотвратит появление перехвата.

Это немного сложнее, если вы хотите перехватить все вызовы (а не только те из вашего источника) - это обычно можно сделать с динамической загрузкой и разрешением реальной функции (с вызовами типа dlload- и dlsym-), но я не думаю, что это необходимо в вашем случае.

423
задан wp78de 25 December 2018 в 05:18
поделиться

16 ответов

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

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

загрузка xmlstarlet: http://sourceforge.net/project/showfiles.php?group_id=66612&package_id=64589

1
ответ дан anjanb 25 December 2018 в 05:18
поделиться

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

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();
} 
9
ответ дан Community 25 December 2018 в 05:18
поделиться

Я обнаружил, что в Java 1.6.0_32 нормальный метод для симпатичной печати строки XML (с использованием Transformer с нулевым или тождественным 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();
    }
1
ответ дан JFK 25 December 2018 в 05:18
поделиться

Вот способ сделать это, используя 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();
18
ответ дан Synesso 25 December 2018 в 05:18
поделиться
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. Поиск обходных решений, характерных для Вашей платформы.

247
ответ дан Jean-François Savard 25 December 2018 в 05:18
поделиться

Использование 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)));
2
ответ дан BijanE 25 December 2018 в 05:18
поделиться

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

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

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

11
ответ дан rzymek 25 December 2018 в 05:18
поделиться

Поскольку вы начинаете с 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));
}
13
ответ дан Kevin Hakanson 25 December 2018 в 05:18
поделиться

Просто для дальнейшего использования, вот решение, которое сработало для меня (благодаря комментарию, который @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());
8
ответ дан Michael 25 December 2018 в 05:18
поделиться

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

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>
123
ответ дан Community 25 December 2018 в 05:18
поделиться

В прошлом я довольно печатал, используя метод 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();
}
31
ответ дан mlo55 25 December 2018 в 05:18
поделиться

Вот ответ на мой собственный вопрос. Я объединил ответы из различных результатов, чтобы написать класс, который прекрасно печатает 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));
    }

}
133
ответ дан Steve McLeod 25 December 2018 в 05:18
поделиться

Относительно комментария, что "необходимо сначала создать дерево DOM": Нет, Вы нуждаетесь не и не должны делать этого.

Вместо этого создайте StreamSource (новый StreamSource (новый StringReader (ул.)), и канал, который к преобразователю идентификационных данных упомянул. Это будет использовать синтаксический анализатор SAX, и результат будет намного быстрее. Создание промежуточного дерева чисто служебный для этого случая. Иначе находящийся на вершине рейтинга ответ хорош.

8
ответ дан StaxMan 25 December 2018 в 15:18
поделиться
  • 1
    Добавление пользователя с " СОЗДАЙТЕ ПОЛЬЗОВАТЕЛЬСКОГО казначея С ПАРОЛЕМ ' password' " и делая " psql-U puser" оценивает к ошибке " psql: ФАТАЛЬНЫЙ: база данных " puser" не делает exist" – grigoryvp 1 December 2010 в 20:41

Если можно использовать стороннюю 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.

12
ответ дан 22 November 2019 в 23:16
поделиться

Кевин Хакансон сказал: "Однако, если вы знаете, что ваша строка 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
}
11
ответ дан 22 November 2019 в 23:16
поделиться

У меня была та же проблема, и у меня большой успех с 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();
2
ответ дан 22 November 2019 в 23:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: