2010-04-02 5 views
10

Estoy buscando una herramienta XSLT o de línea de comandos (o código C# que se pueda convertir en una herramienta de línea de comandos, etc.) para Windows que hará una impresión bonita en XML. En concreto, quiero uno que tiene la capacidad de poner atributos uno a una línea, algo así como:¿Hay una hoja de estilo o una herramienta de línea de comandos de Windows para el formato XML controlable, especificando los atributos uno por línea?

<Node> 
    <ChildNode 
     value1='5' 
     value2='6' 
     value3='happy' /> 
</Node> 

No tiene por qué ser exactamente así, pero quiero utilizarlo para un XML El archivo que tiene nodos con docenas de atributos y su distribución a través de múltiples líneas los hace más fáciles de leer, editar y diferencia de texto.

NOTA: Creo que mi solución preferida es una hoja XSLT que puedo pasar a través de un método C#, aunque una herramienta de línea de comandos de Windows también es buena.

+0

Actualicé mi respuesta y publiqué un ejemplo. – newtover

+0

@stafford - Todavía vería a Tidy (ver mi respuesta a continuación). Es una práctica herramienta de línea de comandos para tener en tu repertorio si trabajas con XML, incluso si no terminas usándolo para este problema en particular. –

+0

@Bert F: Gracias, lo haré. He encontrado que las herramientas de impresión bonita y canonicalización son muy útiles para mantener en la caja de herramientas en otros dominios. –

Respuesta

9

He aquí una pequeña muestra de C#, que puede ser utilizado directamente por el código, o integrado en un exe y llamó a la línea de comand como "myexe from.xml to.xml": entrada

using System.Xml; 

    static void Main(string[] args) 
    { 
     XmlWriterSettings settings = new XmlWriterSettings { 
      NewLineHandling = NewLineHandling.Entitize, 
      NewLineOnAttributes = true, Indent = true, IndentChars = " ", 
      NewLineChars = Environment.NewLine 
     }; 

     using (XmlReader reader = XmlReader.Create(args[0])) 
     using (XmlWriter writer = XmlWriter.Create(args[1], settings)) { 
      writer.WriteNode(reader, false); 
      writer.Close(); 
     } 
    } 

muestra:

<Node><ChildNode value1='5' value2='6' value3='happy' /></Node> 

salida de ejemplo (tenga en cuenta que puede eliminar el <?xml ... con settings.OmitXmlDeclaration):

<?xml version="1.0" encoding="utf-8"?> 
<Node> 
    <ChildNode 
    value1="5" 
    value2="6" 
    value3="happy" /> 
</Node> 

Tenga en cuenta que si quieres una cadena en vez de escribir en un archivo, sólo cambio con StringBuilder:

StringBuilder sb = new StringBuilder(); 
using (XmlReader reader = XmlReader.Create(new StringReader(oldXml))) 
using (XmlWriter writer = XmlWriter.Create(sb, settings)) { 
    writer.WriteNode(reader, false); 
    writer.Close(); 
} 
string newXml = sb.ToString(); 
+0

Esto era lo que necesitaba, gracias. No sabía que existían los XmlWriterSettings, yo estaba trabajando con un XDocument que tiene una .Save y un conjunto muy limitado de SaveOptions ... Una vez que supe C# tenía los XmlWriterSettings objeto y NewLineOnAttributes (obviamente), que era bueno para ir. –

0

XML Notepad 2007 puede hacerlo manualmente ... déjame ver si se puede escribir.

Nop ... puede lanzarlo así:

XmlNotepad.exe a.xml 

El resto es sólo hacer clic en el botón de guardar. Power Shell, otras herramientas pueden automatizar eso.

+0

@Hamish Grubijan, eso probablemente funcionaría, pero la automatización de las GUI es tremendamente hacky, ¡debe haber una manera más fácil! –

0

sólo tiene que utilizar este XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" encoding="ISO-8859-1"/> 
    <xsl:param name="indent-increment" select="' '"/> 

    <xsl:template name="newline"> 
    <xsl:text disable-output-escaping="yes"> 
</xsl:text> 
    </xsl:template> 

    <xsl:template match="comment() | processing-instruction()"> 
    <xsl:param name="indent" select="''"/> 
    <xsl:call-template name="newline"/>  
    <xsl:value-of select="$indent"/> 
    <xsl:copy /> 
    </xsl:template> 

    <xsl:template match="text()"> 
    <xsl:param name="indent" select="''"/> 
    <xsl:call-template name="newline"/>  
    <xsl:value-of select="$indent"/> 
    <xsl:value-of select="normalize-space(.)"/> 
    </xsl:template> 

    <xsl:template match="text()[normalize-space(.)='']"/> 

    <xsl:template match="*"> 
    <xsl:param name="indent" select="''"/> 
    <xsl:call-template name="newline"/>  
    <xsl:value-of select="$indent"/> 
     <xsl:choose> 
     <xsl:when test="count(child::*) > 0"> 
     <xsl:copy> 
     <xsl:copy-of select="@*"/> 
     <xsl:apply-templates select="*|text()"> 
      <xsl:with-param name="indent" select="concat ($indent, $indent-increment)"/> 
     </xsl:apply-templates> 
     <xsl:call-template name="newline"/> 
     <xsl:value-of select="$indent"/> 
     </xsl:copy> 
     </xsl:when>  
     <xsl:otherwise> 
     <xsl:copy-of select="."/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:template>  
</xsl:stylesheet> 

O, como otra opción, aquí es un script de Perl: http://software.decisionsoft.com/index.html

+0

Eso se ve bien. Para completar la tarea, ¿hay un procesador xslt de línea de comando/¿puedo usar C# para procesar XML con un XSLT? Tanto –

+0

, pero procesador XSLT de línea de comandos sajón (http://saxon.sourceforge.net/) debería ser suficiente :) – glebm

+0

Yo probé y no parece funcionar. Sangra cada NODO, pero no hace una nueva línea/sangría en cada ATRIBUTO. Reemplacé la cadena de incremento de sangrado con XYXY para asegurarme de que estaba procesando, y así era, pero los atributos todavía están en una línea larga. –

2

hay una herramienta, que puede dividir atributos a uno por línea: xmlpp. Es un script de Perl, por lo que tendrás que instalar perl. Uso:

perl xmlpp.pl -t input.xml 

También puede determinar el orden de los atributos mediante la creación de un archivo llamado attributeOrdering.txt, y llamando perl xmlpp.pl -s -t input.xml. Para más opciones, use perl xmlpp.pl -h

Espero que no tenga muchos errores, pero me ha funcionado hasta ahora.

+0

@chris_l - gracias. mi equipo es todos los desarrolladores de ASP.NET C#, por lo que nadie tendrá instalado Perl. Conseguir que lo instalen es probablemente posible pero no ideal. ¡Pero lo probé y hace lo que se supone que debe hacer! –

0

Puede implementar una aplicación SAX simple que copiará todo as is y los atributos de sangría como desee.

UPD:

SAX representa Simple API for XML. Es un modelo push de análisis XML (un ejemplo clásico de patrón de diseño de Builder). La API está presente en la mayoría de las plataformas de desarrollo actuales (aunque la biblioteca nativa de la clase .Net carece de una, tiene intefaz XMLReader)

Aquí hay una implementación sin procesar en python, es bastante críptica pero puede realizar la idea principal.

from sys import stdout 
from xml.sax import parse 
from xml.sax.handler import ContentHandler 
from xml.sax.saxutils import escape 

class MyHandler(ContentHandler): 

    def __init__(self, file_, encoding): 
     self.level = 0 
     self.elem_indent = ' ' 

     # should the next block make a line break 
     self._allow_N = False 
     # whether the opening tag was closed with > (to allow />) 
     self._tag_open = False 

     self._file = file_ 
     self._encoding = encoding 

    def _write(self, string_): 
     self._file.write(string_.encode(self._encoding)) 

    def startElement(self, name, attrs): 
     if self._tag_open: 
      self._write('>') 
      self._tag_open = False 

     if self._allow_N: 
      self._write('\n') 
      indent = self.elem_indent * self.level 
     else: 
      indent = '' 
     self._write('%s<%s' % (indent, name)) 

     # attr indent equals to the element indent plus ' ' 
     attr_indent = self.elem_indent * self.level + ' ' 
     for name in attrs.getNames(): 
      # write indented attribute one per line 
      self._write('\n%s%s="%s"' % (attr_indent, name, escape(attrs.getValue(name)))) 

     self._tag_open = True 

     self.level += 1 
     self._allow_N = True 

    def endElement(self, name): 
     self.level -= 1 
     if self._tag_open: 
      self._write(' />') 
      self._tag_open = False 
      return 

     if self._allow_N: 
      self._write('\n') 
      indent = self.elem_indent * self.level 
     else: 
      indent = '' 
     self._write('%s</%s>' % (indent, name)) 
     self._allow_N = True 

    def characters(self, content): 
     if self._tag_open: 
      self._write('>') 
      self._tag_open = False 

     if content.strip(): 
      self._allow_N = False 
      self._write(escape(content)) 
     else: 
      self._allow_N = True 


if __name__ == '__main__': 
    parser = parse('test.xsl', MyHandler(stdout, stdout.encoding)) 
+0

@newtover: ¿Puedes proporcionar un poco más de información? Estoy seguro de que es cierto, pero si supiera cómo, no habría hecho la pregunta. :) –

4

Pruebe Tidy en SourceForge. Aunque a menudo se usa en [X] HTML, lo he usado con éxito en XML antes, solo asegúrese de usar la opción -xml.

http://tidy.sourceforge.net/docs/tidy_man.html

Tidy lee HTML, XHTML y archivos XML y escribe limpiado marcado. ...Para archivos XML genéricos, Tidy se limita a corregir errores básicos de buena formación y impresión bonita.

Las personas se han portado a varias plataformas y está disponible como una biblioteca ejecutable e invocable.

Tidy tiene un montón de opciones que incluyen:

http://tidy.sourceforge.net/docs/quickref.html#indent-attributes

guión-atributos
Tipo superior: Boolean
por defecto: no Ejemplo: s/n, sí/no, t/f , verdadero/falso, 1/0
Esta opción especifica si Tidy debe comenzar cada atributo en una nueva línea.

Una advertencia:

Soporte limitado para XML

procesadores XML que cumplen con la recomendación XML 1.0 de W3C son muy exigentes con qué archivos van a aceptar. Tidy puede ayudarte a corregir los errores que hacen que tus archivos XML sean rechazados. Tidy aún no reconoce todas las características XML, por ejemplo, no comprende secciones CDATA o subconjuntos DTD.

Pero sospecho que a menos que su XML sea realmente avanzado, la herramienta debería funcionar bien.

9

Aquí hay un script de PowerShell para hacerlo. Se necesita la siguiente entrada:

<?xml version="1.0" encoding="utf-8"?> 
<Node> 
    <ChildNode value1="5" value2="6" value3="happy" /> 
</Node> 

... y produce esto como resultado:

<?xml version="1.0" encoding="utf-8"?> 
<Node> 
    <ChildNode 
    value1="5" 
    value2="6" 
    value3="happy" /> 
</Node> 

Aquí van:

param(
    [string] $inputFile = $(throw "Please enter an input file name"), 
    [string] $outputFile = $(throw "Please supply an output file name") 
) 

$data = [xml](Get-Content $inputFile) 

$xws = new-object System.Xml.XmlWriterSettings 
$xws.Indent = $true 
$xws.IndentChars = " " 
$xws.NewLineOnAttributes = $true 

$data.Save([Xml.XmlWriter]::Create($outputFile, $xws)) 

Tome ese guión, guárdelo como C: \ formatxml.ps1. Luego, a partir de un tipo del aviso PowerShell lo siguiente:

C:\formatxml.ps1 C:\Path\To\UglyFile.xml C:\Path\To\NeatAndTidyFile.xml 

Este script es básicamente utilizando el.NET Framework para que pueda migrar esto fácilmente a una aplicación C#.

NOTA: Si no ha ejecutado los scripts de PowerShell antes, tendrá que ejecutar el siguiente comando en un símbolo PowerShell elevada antes de que usted será capaz de ejecutar la secuencia de comandos:

Set-ExecutionPolicy RemoteSigned 
única

Usted tiene que hacer esto una vez sin embargo.

Espero que te sea útil.

Cuestiones relacionadas