Java: Como recuar XML gerado pelo Transformer

112

Estou usando o transformador XML integrado do Java para pegar um documento DOM e imprimir o XML resultante. O problema é que ele não está recuando o texto, apesar de ter definido o parâmetro "recuar" explicitamente.

Código de amostra

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 desejado

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

Pensamentos?

Mike
fonte

Respostas:

215

Você precisa habilitar 'INDENT' e definir o valor de indentação para o transformador:

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

Atualizar:


Referência: como remover nós de texto apenas com espaço em branco de um DOM antes da serialização?

(Muito obrigado a todos os membros, especialmente @ marc-novakowski, @ james-murty e @saad) :

adatapost
fonte
70
Parece bobo para mim que o recuo padrão seja 0, mas além disso INDENT=yeseu também tive que adicionar isso:t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
lapo
1
Cuidado. Esta propriedade de indentação não funciona em java 5.
Funciona
4
Se houver nós internos com várias linhas, você também pode recuar a parte interna? Apenas usar isso não recua os nós internos.
eipark
1
@eipark com stackoverflow.com/a/979606/837530 , removi os espaços em branco e agora recuo como um encanto
Sa'ad
1
@lapo se o seu provedor for xalan (o que provavelmente será, se funcionar), ele estará disponível comoorg.apache.xml.serializer.OutputPropertiesFactory.S_KEY_INDENT_AMOUNT
OrangeDog
21

Nenhuma das soluções sugeridas funcionou para mim. Continuei então em busca de uma solução alternativa, que acabou sendo uma mistura das duas antes mencionadas e uma terceira etapa.

  1. definir o número de indentação na fábrica do transformador
  2. habilite o recuo no transformador
  3. envolva o otuputstream com um gravador (ou bufferedwriter)
//(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"));

Você deve fazer (3) para contornar um comportamento "bugado" do código de tratamento xml.

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

(Se eu citei minha fonte incorretamente, por favor me avise)

mabac
fonte
3
A que "out" está se referindo na última linha?
mujimu
Você precisa criar um novo inteiro usando um construtor?
Benjineer
Estou supondo porque seu provedor não é Xalan. Você pode verificar o que você TransformerFactoryrealmente é para que os outros saibam.
OrangeDog
A etapa 3, usando um Writercomo saída, é essencial.
Erickson
14

O código a seguir está funcionando para mim com o Java 7. Eu defino o recuo (sim) e o valor de recuo (2) no transformador (não na fábrica do transformador) para fazê-lo funcionar.

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

A solução de @mabac para definir o atributo não funcionou para mim, mas o comentário de @ lapo se mostrou útil.

remipod
fonte
8

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

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

fonte
Esta é uma classe interna, então seu código não será portável para outras JVMs (ou mesmo mais novas).
OrangeDog
5

Se você quiser o recuo, você deve especificá-lo para o TransformerFactory.

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

Usei a biblioteca Xerces (Apache) em vez de mexer com o Transformer. Depois de adicionar a biblioteca, adicione o código abaixo.

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);
setesevens
fonte
Sim. Tentei todas as outras abordagens com o Transformer, mas todas quebradas. Toda a biblioteca W3C está uma bagunça. Xerces funcionou.
Tuntable de
3

Para mim, adicionar DOCTYPE_PUBLICfuncionou:

transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,"yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "10");
Vikas Chowdhury
fonte
transformer.setOutputProperty (OutputKeys.DOCTYPE_PUBLIC, "sim"); é a chave
silentsudo