Java: Cómo sangrar XML generado por Transformer


112

Estoy usando el transformador XML integrado de Java para tomar un documento DOM e imprimir el XML resultante. El problema es que no está sangrando el texto en absoluto a pesar de haber establecido el parámetro "sangría" explícitamente.

Código de muestra

public class TestXML {

 public static void main(String args[]) throws Exception {
  ByteArrayOutputStream s;

  Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
  Transformer t = TransformerFactory.newInstance().newTransformer();

  Element a,b;

  a = d.createElement("a");
  b = d.createElement("b");

  a.appendChild(b);

  d.appendChild(a);

  t.setParameter(OutputKeys.INDENT, "yes");

  s = new ByteArrayOutputStream();

  t.transform(new DOMSource(d),new StreamResult(s));

  System.out.println(new String(s.toByteArray()));

 }
}

resultado

<?xml version="1.0" encoding="UTF-8" standalone="no"?><a><b/></a>

resultado deseado

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<a>
 <b/>
</a>

Pensamientos

Respuestas:


215

Debe habilitar 'INDENT' y establecer la cantidad de sangría para el transformador:

t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

Actualizar:


Referencia: ¿Cómo eliminar los nodos de texto de solo espacios en blanco de un DOM antes de la serialización?

(Muchas gracias a todos los miembros, especialmente a @ marc-novakowski, @ james-murty y @saad) :


70
Me parece una tontería que la sangría predeterminada sea 0, pero además INDENT=yestambién tuve que agregar esto:t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
— lapo

1
Tener cuidado. Esta propiedad de sangría no funciona en java 5. sí en java 7. No lo he probado en java 6
— Hilikus

4
Si hay nodos internos que son múltiples líneas, ¿puede sangrar también la parte interna? El solo uso de esto no sangra los nodos internos.
— eipark

1
@eipark con stackoverflow.com/a/979606/837530 , eliminé los espacios en blanco y ahora sangré como un encanto
— Sa'ad

1
@lapo si su proveedor es xalan (que probablemente lo sea si esto funciona), entonces está disponible comoorg.apache.xml.serializer.OutputPropertiesFactory.S_KEY_INDENT_AMOUNT
— OrangeDog

21

Ninguna de las soluciones sugeridas funcionó para mí. Así que seguí buscando una solución alternativa, que terminó siendo una mezcla de las dos antes mencionadas y un tercer paso.

  1. establecer el número de sangría en el transformerfactory
  2. habilitar la sangría en el transformador
  3. envuelva el otuputstream con un escritor (o escritor en búfer)
//(1)
TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute("indent-number", new Integer(2));

//(2)
Transformer t = tf.newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");

//(3)
t.transform(new DOMSource(doc),
new StreamResult(new OutputStreamWriter(out, "utf-8"));

Debe hacer (3) para solucionar un comportamiento "defectuoso" del código de manejo xml.

Fuente: johnnymac75 @ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446

(Si he citado mi fuente incorrectamente, hágamelo saber)


3
¿A qué se refiere "out" en la última línea?
— mujimu

¿Necesita crear un nuevo entero usando un constructor?
— Benjineer

Supongo que porque su proveedor no es Xalan. ¿Puedes comprobar cuál es tu TransformerFactoryrealidad para que otros sepan?
— OrangeDog

El paso 3, usar Writercomo salida, es esencial.
— erickson

14

El siguiente código me funciona con Java 7. Establecí la sangría (sí) y la cantidad de sangría (2) en el transformador (no en la fábrica de transformadores) para que funcione.

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

La solución de @ mabac para establecer el atributo no funcionó para mí, pero el comentario de @ lapo resultó útil.


8

import com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory

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

Esta es una clase interna, por lo que su código no será portátil a otras JVM (o incluso más nuevas).
— OrangeDog

5

Si desea la sangría, debe especificarla en el TransformerFactory.

TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute("indent-number", new Integer(2));
Transformer t = tf.newTransformer();

4

Usé la biblioteca Xerces (Apache) en lugar de jugar con Transformer. Una vez que agregue la biblioteca, agregue el código a continuación.

OutputFormat format = new OutputFormat(document);
format.setLineWidth(65);
format.setIndenting(true);
format.setIndent(2);
Writer outxml = new FileWriter(new File("out.xml"));
XMLSerializer serializer = new XMLSerializer(outxml, format);
serializer.serialize(document);

Si. Probé todos los demás enfoques con el Transformer pero todos se rompieron. Toda la biblioteca del W3C es un desastre. Xerces funcionó.
— Tuntable

3

Para mí, la adición DOCTYPE_PUBLICfuncionó:

transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,"yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "10");

transformer.setOutputProperty (OutputKeys.DOCTYPE_PUBLIC, "sí"); es la clave
— silentsudo
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.