Java: Jak wciskać kod XML generowany przez Transformer

112

Używam wbudowanego w Javę transformatora XML, aby wziąć dokument DOM i wydrukować wynikowy XML. Problem polega na tym, że w ogóle nie wcina się tekstu, pomimo jawnego ustawienia parametru „indent”.

przykładowy kod

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

 }
}

wynik

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

pożądany rezultat

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

Myśli?

Mikrofon
źródło

Odpowiedzi:

215

Musisz włączyć „WCIĘCIE” i ustawić wielkość wcięcia dla transformatora:

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

Aktualizacja:


Odniesienie: Jak usunąć węzły tekstowe zawierające tylko białe znaki z DOM przed serializacją?

(Podziękowania dla wszystkich członków, zwłaszcza @ marc-novakowski, @ james-murty i @saad) :

adatapost
źródło
70
Wydaje mi się głupie, że domyślne wcięcie to 0, ale oprócz tego INDENT=yesmusiałem też dodać to:t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
lapo
1
Strzec się. Ta nieruchomość wcięcie nie działa w Javie 5. robi w Javie 7. Nie próbowałem w Java 6
Hilikus
4
Jeśli istnieją węzły wewnętrzne składające się z wielu linii, czy możesz również wciąć część wewnętrzną? Samo użycie tego nie powoduje wcięcia węzłów wewnętrznych.
eipark
1
@eipark with stackoverflow.com/a/979606/837530 , usunąłem spacje, a teraz wcięcia jak urok
Sa'ad
1
@lapo, jeśli twoim dostawcą jest xalan (co prawdopodobnie jest, jeśli to działa), to jest dostępne jakoorg.apache.xml.serializer.OutputPropertiesFactory.S_KEY_INDENT_AMOUNT
OrangeDog
21

Żadne z sugerowanych rozwiązań nie zadziałało. Kontynuowałem więc poszukiwanie alternatywnego rozwiązania, które okazało się połączeniem dwóch wcześniej wspomnianych i trzeciego kroku.

  1. ustaw numer wcięcia w transformerfactory
  2. włącz wcięcie w transformatorze
  3. zawiń otuputstream pisarzem (lub buforowanym pisarzem)
//(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"));

Musisz (3) obejść "błędne" zachowanie kodu obsługującego XML.

Źródło: johnnymac75 @ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446

(Jeśli nieprawidłowo zacytowałem moje źródło, daj mi znać)

mabac
źródło
3
Do czego odnosi się „out” w ostatniej linii?
mujimu
Czy musisz utworzyć nową liczbę całkowitą za pomocą konstruktora?
Benjineer,
Zgaduję, ponieważ twoim dostawcą nie jest Xalan. Czy możesz sprawdzić, czym TransformerFactorywłaściwie jest, aby inni wiedzieli.
OrangeDog
Krok 3, użycie Writerjako wyjścia, jest niezbędny.
erickson
14

Poniższy kod działa dla mnie z Javą 7. Ustawiłem wcięcie (tak) i wielkość wcięcia (2) na transformatorze (nie fabryce transformatorów), aby działał.

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

Rozwiązanie @ mabac do ustawiania atrybutu nie działało w moim przypadku, ale komentarz @ lapo okazał się pomocny.

remipod
źródło
8

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

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

źródło
Jest to klasa wewnętrzna, więc Twój kod nie będzie przenośny na inne (lub nawet nowsze) maszyny JVM.
OrangeDog
5

Jeśli chcesz wcięcia, musisz określić je jako TransformerFactory.

TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute("indent-number", new Integer(2));
Transformer t = tf.newTransformer();
lucbelanger
źródło
4

Użyłem biblioteki Xerces (Apache) zamiast bawić się Transformerem. Po dodaniu biblioteki dodaj poniższy kod.

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);
sevensevens
źródło
Tak. Wypróbowałem wszystkie inne podejścia z Transformerem, ale wszystkie się zepsuły. Cała biblioteka W3C to bałagan. Xerces działał.
Tuntable
3

U mnie dodanie DOCTYPE_PUBLICzadziałało:

transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,"yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "10");
Vikas Chowdhury
źródło
transformer.setOutputProperty (OutputKeys.DOCTYPE_PUBLIC, "tak"); jest kluczem
silentsudo