Есть ли таблица стилей или инструмент командной строки Windows для контролируемого форматирования XML, в частности, размещение атрибутов по одной на строку?

Я ищу XSLT или инструмент командной строки (или код C#, который можно превратить в инструмент командной строки и т. Д.) Для Windows, который будет выполнять красивую печать XML. В частности, я хочу тот, который имеет возможность помещать атрибуты в строку, что-то вроде:

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

Это не обязательно должно быть ТОЧНО, но я хочу использовать его для XML-файла, в котором есть узлы с десятками атрибутов, а распределение по нескольким строкам упрощает их чтение, редактирование и разность текста.

ПРИМЕЧАНИЕ. Я думаю, что моим предпочтительным решением является лист XSLT, который я могу пройти с помощью метода C#, хотя инструмент командной строки Windows также хорош.

7 ответов

Решение

Вот небольшой пример C#, который может быть использован непосредственно вашим кодом или встроен в exe и вызван в командной строке как "myexe from.xml to.xml":

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

Пример ввода:

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

Пример вывода (обратите внимание, вы можете удалить <?xml ... с settings.OmitXmlDeclaration):

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

Обратите внимание, что если вам нужна строка, а не запись в файл, просто поменяйте местами 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();

Вот сценарий PowerShell, чтобы сделать это. Требуется следующий вход:

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

... и выдает это как вывод:

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

Ну вот:

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

Возьмите этот скрипт, сохраните его как C: \ formatxml.ps1. Затем из командной строки PowerShell введите следующее:

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

Этот скрипт в основном использует.NET Framework, поэтому вы можете легко перенести его в приложение C#.

ПРИМЕЧАНИЕ. Если вы ранее не запускали сценарии из PowerShell, вам потребуется выполнить следующую команду в командной строке PowerShell с повышенными правами, прежде чем вы сможете выполнить сценарий:

Set-ExecutionPolicy RemoteSigned

Вы должны сделать это только один раз, хотя.

Я надеюсь, что это полезно для вас.

Попробуйте Tidy на SourceForge. Хотя он часто используется в [X]HTML, я уже успешно использовал его в XML - просто убедитесь, что вы используете -xml вариант.

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

Tidy читает файлы HTML, XHTML и XML и пишет очищенную разметку.... Для общих XML-файлов Tidy ограничивается исправлением основных ошибок правильной формы и красивой печатью.

Люди перенесли на несколько платформ, и он доступен как исполняемая и вызываемая библиотека.

У Tidy есть куча вариантов, в том числе:

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

отступ-атрибуты
Тип верха: логическое значение
По умолчанию: нет Пример: y / n, да / нет, t/f, true/false, 1/0
Эта опция указывает, должен ли Tidy начинать каждый атрибут с новой строки.

Одно предостережение:

Ограниченная поддержка XML

Процессоры XML, совместимые с рекомендацией W3C XML 1.0, очень требовательны к тому, какие файлы они будут принимать. Tidy может помочь вам исправить ошибки, которые приводят к отклонению ваших файлов XML. Tidy еще не распознает все возможности XML, например, он не понимает разделы CDATA или подмножества DTD.

Но я подозреваю, что если ваш XML не является действительно продвинутым, инструмент должен работать нормально.

Существует инструмент, который может разделять атрибуты по одному на строку: xmlpp. Это скрипт на Perl, поэтому вам придется установить Perl. Использование:

perl xmlpp.pl -t input.xml

Вы также можете определить порядок атрибутов, создав файл attributeOrdering.txt и вызвав perl xmlpp.pl -s -t input.xml, Для большего количества вариантов используйте perl xmlpp.pl -h

Надеюсь, в ней не так много ошибок, но до сих пор это сработало.

Вы можете реализовать простое приложение SAX, которое будет копировать все as is и отступ атрибутов, как вам нравится.

UPD:

SAX обозначает Simple API for XML, Это push-модель парсинга XML (классический пример шаблона проектирования Builder). API присутствует в большинстве современных платформ разработки (хотя в нативной библиотеке классов.Net ее нет, с целочисленным XMLReader)

Вот сырая реализация на python, она довольно загадочная, но вы можете реализовать основную идею.

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

XML Notepad 2007 может сделать это вручную... давайте посмотрим, можно ли его написать в сценарии.

Нет... он может запустить его так:

XmlNotepad.exe a.xml

Остальное просто нажмите кнопку Сохранить. Power Shell, другие инструменты могут автоматизировать это.

Просто используйте этот 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>

Или, как еще один вариант, вот скрипт perl: http://software.decisionsoft.com/index.html

Другие вопросы по тегам