2011-01-06 9 views
27

¿Realmente no hay forma de escribir directamente XML formateado usando javax.xml.stream.XMLStreamWriter (Java SE 6) ??? Esto es realmente increíble, ya que otras API XML como JAXB y algunas bibliotecas DOM pueden hacer esto. Incluso el .NET XMLStreamWriter equivalente es capaz de este AFAIK (si mal no recuerdo la clase es System.Xml.XmlTextWriter).XMLStreamWriter: indentation

Esto significa que la única opción que tengo es volver a analizar el XML para generar una salida formateada ??

ej .:

  StringWriter sw = new StringWriter(); 
    XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory(); 
    XMLStreamWriter xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(sw); 
    writeXml(xmlStreamWriter); 
    xmlStreamWriter.flush(); 
    xmlStreamWriter.close(); 

    TransformerFactory factory = TransformerFactory.newInstance(); 

    Transformer transformer = factory.newTransformer(); 
    transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 
    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 

    StringWriter formattedStringWriter = new StringWriter(); 
    transformer.transform(new StreamSource(new StringReader(sw.toString())), new StreamResult(formattedStringWriter)); 
    System.out.println(formattedStringWriter); 

El problema de esta solución es la propiedad "{http://xml.apache.org/xslt}indent-amount". No encontré ninguna documentación al respecto y no parece garantizarse que sea portátil.

¿Qué otras opciones tengo, si quiero hacer esto con las clases estándar de Java 6? ¿Cree un gráfico de objetos JAXB o DOM solo para una bonita impresión?

Respuesta

6

Puede agregar el código necesario para formatear su documento en su método writeXml. Simplemente mantenga un contador de profundidad (para indicar los niveles de anidación). Luego, antes de escribirStartElement y después de escribirEndElement, use el índice de profundidad para insertar una sangría.

for(int x=0; x<depth; x++) { 
    xmlStreamWriter.writeCharacters(" "); 
} 
+0

No puedo encontrar tal método de escritura de texto?!? http://download.oracle.com/javase/6/docs/api/javax/xml/stream/XMLStreamWriter.html – Puce

+0

Hmm, tal vez con writeCharacters, pero pensé que esto solo para contenido de elementos ... – Puce

+0

Está en lo correcto, debería haber sido writeCharacters. Este método se puede usar para insertar texto en cualquier parte de un documento, no solo dentro de un solo elemento. –

1

Tiene razón en que las interfaces estándar java.xml proporcionan poco o ningún control sobre la serialización, a pesar de la implementación subyacente (Apache Xerces) hace. Tuve que resolver este problema y lo mejor que se me ocurrió fue incluir una copia de Xerces y utilizar las clases org.apache.xml.serialize.

+1

Sí, he leído sobre tales soluciones, pero desde Xerces 2.9.0 este paquete ha quedado en desuso: http://xerces.apache.org/xerces2-j/javadocs/other/org/apache/xml/serialize/package -summary.html – Puce

34

Este exacta question ha sido respondida hace unos meses y one of the answers es utilizar la clase IndentingXMLStreamWriter:

XMLOutputFactory xmlof = XMLOutputFactory.newInstance(); 
XMLStreamWriter writer = new IndentingXMLStreamWriter(xmlof.createXMLStreamWriter(out)); 

Es una buena solución por lo que el código va, pero cuidado: este es un com.sun . * clase, no hay ninguna garantía de su disponibilidad general ...

+1

Como dijo, desafortunadamente esto no es una API publicada. – Puce

+1

La clase 'IndentingXMLStreamWriter' y su clase base' DelegatingXMLStreamWriter' se pueden abrir con un descompilador (IntelliJ IDEA tiene una función incorporada en la actualidad) y se guardan en formato fuente como parte de nuestro propio proyecto. Entonces, su 'inédito' no es realmente un problema. Si hace esto, asegúrese de corregir un error en el código del sol: en el método 'onEmptyElement()', la llamada a 'writeCharacters (" \ n ");' debe ser 'super.writeCharacters (" \ n "); 'en cambio. –

2

Con Spring batch esto requiere una subclase ya que este JIRA BATCH-1867

public class IndentingStaxEventItemWriter<T> extends StaxEventItemWriter<T> { 

    @Setter 
    @Getter 
    private boolean indenting = true; 

    @Override 
    protected XMLEventWriter createXmlEventWriter(XMLOutputFactory outputFactory, Writer writer) throws XMLStreamException { 
    if (isIndenting()) { 
     return new IndentingXMLEventWriter(super.createXmlEventWriter(outputFactory, writer)); 
    } 
    else { 
     return super.createXmlEventWriter(outputFactory, writer); 
    } 
    } 

} 

Pero esto requiere una dependencia additionnal porque Spring Batch no incluye el código para sangrar la salida StAX:

<dependency> 
    <groupId>net.java.dev.stax-utils</groupId> 
    <artifactId>stax-utils</artifactId> 
    <version>20070216</version> 
</dependency> 
+1

La parte interesante es la biblioteca stax-utils, que proporciona el IndentingXMLEventWriter y parece ser independiente de Spring Batch. ¡Gracias por la pista! – Puce

2

Sólo para que conste, Saxon le permite obtener un XMLStreamWriter serialización a través de la interfaz de s9api:

Processor p = new Processor(); 
Serializer s = p.newSerializer(); 
s.setOutputProperty(Property.INDENT, "yes"); 
XMLStreamWriter w = s.getXMLStreamWriter(); 

y el serializador expone todas las propiedades de salida definidas por XSLT (incluyendo "sangría") más algunos específicos de Saxon.

+0

Una versión un poco más completa aquí: http://stackoverflow.com/questions/10105187/java-indentingxmlstreamwriter-alternative/30637501#30637501 –

2

No tengo respuesta teniendo en cuenta "{http://xml.apache.org/xslt}indent-amount" propiedad. Pero puede usar transformadores de alguna manera portátil sin la necesidad de volver a rastrear todos los resultados. Puede usar algo como el código siguiente para crear impresiones bonitas XMLStreamWriter.

public static void main(String[] args) { 
    XMLStreamWriter xmlStreamWriter = createXMLStreamWriter(new OutputStreamWriter(System.out)); 
    writeXml(xmlStreamWriter); 
} 

private static XMLStreamWriter createXMLStreamWriter(Writer textWriter) throws XMLStreamException { 
    SAXTransformerFactory xmlTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); 
    TransformerHandler transformingSAXHandler; 
    try { 
     transformingSAXHandler = xmlTransformerFactory.newTransformerHandler(); 
    } catch (TransformerConfigurationException e) { 
     throw new XMLStreamException(e); 
    } 
    Transformer xmlTransformer = transformingSAXHandler.getTransformer(); 
    xmlTransformer.setOutputProperty(OutputKeys.INDENT, "yes"); 
    transformingSAXHandler.setResult(new StreamResult(textWriter)); 

    XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); 
    return xmlOutputFactory.createXMLStreamWriter(new SAXResult(transformingSAXHandler)); 
} 
+0

La última línea provoca esto: 'java.lang.UnsupportedOperationException: Resultado del tipo javax.xml .transform.sax.SAXResult @ 184f6be2 no es compatible. Los tipos de resultados soportados son: DOMResult, StAXResult y StreamResult'. Usamos Java-versión 1.8u74, ¿verdad? – sjngm

+0

Creo que debería funcionar cuando la implementación subyacente es Apache Xerces, pero no se garantiza que funcione con otras implementaciones ... –

Cuestiones relacionadas