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

Miguel
fuente

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) :

adatapost
fuente
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)

mabac
fuente
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.

remipod
fuente
8

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

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

fuente
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();
Lucbelanger
fuente
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);
siete cielos
fuente
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");
Vikas Chowdhury
fuente
transformer.setOutputProperty (OutputKeys.DOCTYPE_PUBLIC, "sí"); es la clave
silentsudo