Существует ли таблица стилей или инструмент командной строки Windows для контролируемого форматирования XML, в частности, размещение атрибутов по одной на строку? - PullRequest
13 голосов
/ 02 апреля 2010

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

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

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

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

Ответы [ 7 ]

11 голосов
/ 18 апреля 2010

Вот небольшой пример C #, который может быть использован непосредственно вашим кодом или встроен в исполняемый файл и вызван в командной строке как "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();
11 голосов
/ 18 апреля 2010

Вот скрипт 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

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

Надеюсь, это вам пригодится.

5 голосов
/ 17 апреля 2010

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

http://tidy.sourceforge.net/#docs

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

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

Tidy имеет кучу опций , в том числе:

http://api.html -tidy.org / аккуратные / quickref_5.0.0.html # отступа

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

Одна оговорка:

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

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

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

2 голосов
/ 12 апреля 2010

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

perl xmlpp.pl -t input.xml

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

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

0 голосов
/ 12 апреля 2010

Вы можете реализовать простое приложение 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))
0 голосов
/ 02 апреля 2010

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

0 голосов
/ 02 апреля 2010

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

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

XmlNotepad.exe a.xml

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

...